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,1701 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Action handler
|
|
5
|
+
==============
|
|
6
|
+
|
|
7
|
+
The :mod:`datalab.gui.actionhandler` module handles all application actions
|
|
8
|
+
(menus, toolbars, context menu). These actions point to DataLab panels, processors,
|
|
9
|
+
objecthandler, ...
|
|
10
|
+
|
|
11
|
+
Utility classes
|
|
12
|
+
---------------
|
|
13
|
+
|
|
14
|
+
.. autoclass:: SelectCond
|
|
15
|
+
:members:
|
|
16
|
+
|
|
17
|
+
.. autoclass:: ActionCategory
|
|
18
|
+
:members:
|
|
19
|
+
|
|
20
|
+
Handler classes
|
|
21
|
+
---------------
|
|
22
|
+
|
|
23
|
+
.. autoclass:: SignalActionHandler
|
|
24
|
+
:members:
|
|
25
|
+
:inherited-members:
|
|
26
|
+
|
|
27
|
+
.. autoclass:: ImageActionHandler
|
|
28
|
+
:members:
|
|
29
|
+
:inherited-members:
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import abc
|
|
37
|
+
import enum
|
|
38
|
+
from collections.abc import Callable, Generator
|
|
39
|
+
from contextlib import contextmanager
|
|
40
|
+
from functools import partial
|
|
41
|
+
from typing import TYPE_CHECKING
|
|
42
|
+
|
|
43
|
+
import sigima.objects as sio
|
|
44
|
+
from guidata.configtools import get_icon
|
|
45
|
+
from guidata.qthelpers import add_actions, create_action
|
|
46
|
+
from qtpy import QtCore as QC
|
|
47
|
+
from qtpy import QtGui as QG
|
|
48
|
+
from qtpy import QtWidgets as QW
|
|
49
|
+
|
|
50
|
+
from datalab.adapters_metadata import GeometryAdapter, TableAdapter, have_results
|
|
51
|
+
from datalab.config import Conf, _
|
|
52
|
+
from datalab.gui import newobject
|
|
53
|
+
from datalab.widgets import fitdialog
|
|
54
|
+
|
|
55
|
+
if TYPE_CHECKING:
|
|
56
|
+
from sigima.objects import ImageObj, SignalObj
|
|
57
|
+
|
|
58
|
+
from datalab.gui.panel.image import ImagePanel
|
|
59
|
+
from datalab.gui.panel.signal import SignalPanel
|
|
60
|
+
from datalab.objectmodel import ObjectGroup
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class SelectCond:
|
|
64
|
+
"""Signal or image select conditions"""
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def __compat_groups(selected_groups: list[ObjectGroup], min_len: int = 1) -> bool:
|
|
68
|
+
"""Check if groups are compatible"""
|
|
69
|
+
return (
|
|
70
|
+
len(selected_groups) >= min_len
|
|
71
|
+
and all(len(group) == len(selected_groups[0]) for group in selected_groups)
|
|
72
|
+
and all(len(group) > 0 for group in selected_groups)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
# pylint: disable=unused-argument
|
|
77
|
+
def always(
|
|
78
|
+
selected_groups: list[ObjectGroup],
|
|
79
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
80
|
+
) -> bool:
|
|
81
|
+
"""Always true"""
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def exactly_one(
|
|
86
|
+
selected_groups: list[ObjectGroup],
|
|
87
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
88
|
+
) -> bool:
|
|
89
|
+
"""Exactly one signal or image is selected"""
|
|
90
|
+
return len(selected_groups) == 0 and len(selected_objects) == 1
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
# pylint: disable=unused-argument
|
|
94
|
+
def exactly_one_group(
|
|
95
|
+
selected_groups: list[ObjectGroup],
|
|
96
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
97
|
+
) -> bool:
|
|
98
|
+
"""Exactly one group is selected"""
|
|
99
|
+
return len(selected_groups) == 1
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def exactly_one_group_or_one_object(
|
|
103
|
+
selected_groups: list[ObjectGroup],
|
|
104
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
105
|
+
) -> bool:
|
|
106
|
+
"""Exactly one group or one signal or image is selected"""
|
|
107
|
+
return len(selected_groups) == 1 or len(selected_objects) == 1
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
# pylint: disable=unused-argument
|
|
111
|
+
def at_least_one_group_or_one_object(
|
|
112
|
+
sel_groups: list[ObjectGroup],
|
|
113
|
+
sel_objects: list[SignalObj | ImageObj],
|
|
114
|
+
) -> bool:
|
|
115
|
+
"""At least one group or one signal or image is selected"""
|
|
116
|
+
return len(sel_objects) >= 1 or len(sel_groups) >= 1
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
# pylint: disable=unused-argument
|
|
120
|
+
def at_least_one(
|
|
121
|
+
sel_groups: list[ObjectGroup],
|
|
122
|
+
sel_objects: list[SignalObj | ImageObj],
|
|
123
|
+
) -> bool:
|
|
124
|
+
"""At least one signal or image is selected"""
|
|
125
|
+
return len(sel_objects) >= 1 or SelectCond.__compat_groups(sel_groups, 1)
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def at_least_two(
|
|
129
|
+
sel_groups: list[ObjectGroup],
|
|
130
|
+
sel_objects: list[SignalObj | ImageObj],
|
|
131
|
+
) -> bool:
|
|
132
|
+
"""At least two signals or images are selected"""
|
|
133
|
+
return len(sel_objects) >= 2 or SelectCond.__compat_groups(sel_groups, 2)
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
# pylint: disable=unused-argument
|
|
137
|
+
def with_roi(
|
|
138
|
+
selected_groups: list[ObjectGroup],
|
|
139
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
140
|
+
) -> bool:
|
|
141
|
+
"""At least one signal or image has a ROI"""
|
|
142
|
+
return any(obj.roi is not None for obj in selected_objects)
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
# pylint: disable=unused-argument
|
|
146
|
+
def exactly_one_with_roi(
|
|
147
|
+
selected_groups: list[ObjectGroup],
|
|
148
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
149
|
+
) -> bool:
|
|
150
|
+
"""Exactly one signal or image has a ROI"""
|
|
151
|
+
return (
|
|
152
|
+
len(selected_groups) == 0
|
|
153
|
+
and len(selected_objects) == 1
|
|
154
|
+
and selected_objects[0].roi is not None
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
# pylint: disable=unused-argument
|
|
159
|
+
def exactly_one_with_annotations(
|
|
160
|
+
selected_groups: list[ObjectGroup],
|
|
161
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
162
|
+
) -> bool:
|
|
163
|
+
"""Exactly one signal or image has annotations"""
|
|
164
|
+
return (
|
|
165
|
+
len(selected_groups) == 0
|
|
166
|
+
and len(selected_objects) == 1
|
|
167
|
+
and selected_objects[0].has_annotations()
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
# pylint: disable=unused-argument
|
|
172
|
+
def with_annotations(
|
|
173
|
+
selected_groups: list[ObjectGroup],
|
|
174
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
175
|
+
) -> bool:
|
|
176
|
+
"""At least one signal or image has annotations"""
|
|
177
|
+
return any(obj.has_annotations() for obj in selected_objects)
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
# pylint: disable=unused-argument
|
|
181
|
+
def with_results(
|
|
182
|
+
selected_groups: list[ObjectGroup],
|
|
183
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
184
|
+
) -> bool:
|
|
185
|
+
"""At least one signal or image has results"""
|
|
186
|
+
return have_results(selected_objects)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class ActionCategory(enum.Enum):
|
|
190
|
+
"""Action categories"""
|
|
191
|
+
|
|
192
|
+
FILE = enum.auto()
|
|
193
|
+
CREATE = enum.auto()
|
|
194
|
+
EDIT = enum.auto()
|
|
195
|
+
VIEW = enum.auto()
|
|
196
|
+
ROI = enum.auto()
|
|
197
|
+
OPERATION = enum.auto()
|
|
198
|
+
PROCESSING = enum.auto()
|
|
199
|
+
ANALYSIS = enum.auto()
|
|
200
|
+
CONTEXT_MENU = enum.auto()
|
|
201
|
+
PANEL_TOOLBAR = enum.auto()
|
|
202
|
+
VIEW_TOOLBAR = enum.auto()
|
|
203
|
+
SUBMENU = enum.auto() # temporary
|
|
204
|
+
PLUGINS = enum.auto() # for plugins actions
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class BaseActionHandler(metaclass=abc.ABCMeta):
|
|
208
|
+
"""Object handling panel GUI interactions: actions, menus, ...
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
panel: Panel to handle
|
|
212
|
+
panel_toolbar: Panel toolbar (actions related to the panel objects management)
|
|
213
|
+
view_toolbar: View toolbar (actions related to the panel view, i.e. plot)
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
OBJECT_STR = "" # e.g. "signal"
|
|
217
|
+
OBJECT_STR_PLURAL = "" # e.g. "signals"
|
|
218
|
+
|
|
219
|
+
def __init__(
|
|
220
|
+
self,
|
|
221
|
+
panel: SignalPanel | ImagePanel,
|
|
222
|
+
panel_toolbar: QW.QToolBar,
|
|
223
|
+
view_toolbar: QW.QToolBar,
|
|
224
|
+
):
|
|
225
|
+
self.panel = panel
|
|
226
|
+
self.panel_toolbar = panel_toolbar
|
|
227
|
+
self.view_toolbar = view_toolbar
|
|
228
|
+
self.feature_actions = {}
|
|
229
|
+
self.operation_end_actions = None
|
|
230
|
+
self.__category_in_progress: ActionCategory = None
|
|
231
|
+
self.__submenu_in_progress = False
|
|
232
|
+
self.__submenu_stack: list[dict[str, any]] = [] # Stack for nested submenus
|
|
233
|
+
self.__actions: dict[Callable, list[QW.QAction]] = {}
|
|
234
|
+
self.__submenus: dict[str, QW.QMenu] = {}
|
|
235
|
+
# Store reference to ROI remove submenu
|
|
236
|
+
self.roi_remove_submenu: QW.QMenu | None = None
|
|
237
|
+
# Store reference to results delete submenu
|
|
238
|
+
self.results_delete_submenu: QW.QMenu | None = None
|
|
239
|
+
# Store reference to metadata and annotations submenus (for screenshots)
|
|
240
|
+
self.metadata_submenu: QW.QMenu | None = None
|
|
241
|
+
self.annotations_submenu: QW.QMenu | None = None
|
|
242
|
+
# Store reference to show label action (for settings dialog)
|
|
243
|
+
self.show_label_action: QW.QAction | None = None
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def object_suffix(self) -> str:
|
|
247
|
+
"""Object suffix (e.g. "sig" for signal, "ima" for image)"""
|
|
248
|
+
return self.__class__.__name__[:3].lower()
|
|
249
|
+
|
|
250
|
+
def populate_roi_remove_submenu(self) -> None:
|
|
251
|
+
"""Populate the ROI Remove submenu dynamically based on current selection"""
|
|
252
|
+
submenu = self.roi_remove_submenu
|
|
253
|
+
if submenu is None:
|
|
254
|
+
return
|
|
255
|
+
|
|
256
|
+
# Clear existing actions
|
|
257
|
+
submenu.clear()
|
|
258
|
+
|
|
259
|
+
# Get current selected object
|
|
260
|
+
selected_objects = self.panel.objview.get_sel_objects()
|
|
261
|
+
if not selected_objects:
|
|
262
|
+
return
|
|
263
|
+
|
|
264
|
+
obj = selected_objects[0]
|
|
265
|
+
if obj.roi is None or obj.roi.is_empty():
|
|
266
|
+
return
|
|
267
|
+
|
|
268
|
+
# Add individual ROI removal actions
|
|
269
|
+
for i in range(len(obj.roi.single_rois)):
|
|
270
|
+
roi_title = obj.roi.get_single_roi_title(i)
|
|
271
|
+
action = QW.QAction(roi_title, submenu)
|
|
272
|
+
# Use partial to avoid lambda closure issues
|
|
273
|
+
action.triggered.connect(partial(self._remove_single_roi_by_index, i))
|
|
274
|
+
submenu.addAction(action)
|
|
275
|
+
|
|
276
|
+
# Add separator and "Remove all" action
|
|
277
|
+
if len(obj.roi.single_rois) > 0:
|
|
278
|
+
submenu.addSeparator()
|
|
279
|
+
remove_all_action = QW.QAction(_("Remove all"), submenu)
|
|
280
|
+
remove_all_action.triggered.connect(
|
|
281
|
+
self.panel.processor.delete_regions_of_interest
|
|
282
|
+
)
|
|
283
|
+
submenu.addAction(remove_all_action)
|
|
284
|
+
|
|
285
|
+
def _remove_single_roi_by_index(self, roi_index: int) -> None:
|
|
286
|
+
"""Helper method to remove a single ROI by index"""
|
|
287
|
+
self.panel.processor.delete_single_roi(roi_index)
|
|
288
|
+
|
|
289
|
+
def has_metadata_in_clipboard(
|
|
290
|
+
self,
|
|
291
|
+
selected_groups: list[ObjectGroup],
|
|
292
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
293
|
+
) -> bool:
|
|
294
|
+
"""Check if metadata clipboard is not empty"""
|
|
295
|
+
# pylint: disable=unused-argument
|
|
296
|
+
return bool(self.panel.metadata_clipboard)
|
|
297
|
+
|
|
298
|
+
def has_annotations_in_clipboard(
|
|
299
|
+
self,
|
|
300
|
+
selected_groups: list[ObjectGroup],
|
|
301
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
302
|
+
) -> bool:
|
|
303
|
+
"""Check if annotations clipboard is not empty"""
|
|
304
|
+
# pylint: disable=unused-argument
|
|
305
|
+
return bool(self.panel.annotations_clipboard)
|
|
306
|
+
|
|
307
|
+
def populate_results_delete_submenu(self) -> None:
|
|
308
|
+
"""Populate the Results Delete submenu dynamically based on current selection"""
|
|
309
|
+
submenu = self.results_delete_submenu
|
|
310
|
+
if submenu is None:
|
|
311
|
+
return
|
|
312
|
+
|
|
313
|
+
# Clear existing actions
|
|
314
|
+
submenu.clear()
|
|
315
|
+
|
|
316
|
+
# Get current selected objects (including groups)
|
|
317
|
+
objs = self.panel.objview.get_sel_objects(include_groups=True)
|
|
318
|
+
if not objs:
|
|
319
|
+
submenu.setEnabled(False)
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
# Collect all results with their metadata keys and titles
|
|
323
|
+
result_items = [] # List of (metadata_key, title, obj, adapter)
|
|
324
|
+
|
|
325
|
+
for obj in objs:
|
|
326
|
+
# Get all adapters for this object
|
|
327
|
+
for adapter in list(GeometryAdapter.iterate_from_obj(obj)) + list(
|
|
328
|
+
TableAdapter.iterate_from_obj(obj)
|
|
329
|
+
):
|
|
330
|
+
metadata_key = adapter.metadata_key
|
|
331
|
+
title = adapter.title
|
|
332
|
+
result_items.append((metadata_key, title, obj, adapter))
|
|
333
|
+
|
|
334
|
+
if not result_items:
|
|
335
|
+
submenu.setEnabled(False)
|
|
336
|
+
return
|
|
337
|
+
|
|
338
|
+
# Enable submenu since we have results
|
|
339
|
+
submenu.setEnabled(True)
|
|
340
|
+
|
|
341
|
+
# Add individual result deletion actions
|
|
342
|
+
for metadata_key, title, obj, adapter in result_items:
|
|
343
|
+
action = QW.QAction(title, submenu)
|
|
344
|
+
# Use partial to avoid lambda closure issues
|
|
345
|
+
action.triggered.connect(partial(self._delete_single_result, obj, adapter))
|
|
346
|
+
submenu.addAction(action)
|
|
347
|
+
|
|
348
|
+
# Add separator and "Delete all results..." action
|
|
349
|
+
if result_items:
|
|
350
|
+
submenu.addSeparator()
|
|
351
|
+
delete_all_action = QW.QAction(_("Delete all results") + "...", submenu)
|
|
352
|
+
delete_all_action.triggered.connect(self.panel.delete_results)
|
|
353
|
+
submenu.addAction(delete_all_action)
|
|
354
|
+
|
|
355
|
+
def _delete_single_result(
|
|
356
|
+
self, obj: SignalObj | ImageObj, adapter: GeometryAdapter | TableAdapter
|
|
357
|
+
) -> None:
|
|
358
|
+
"""Helper method to delete a single result
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
obj: Object containing the result
|
|
362
|
+
adapter: Adapter for the result to delete
|
|
363
|
+
"""
|
|
364
|
+
adapter.remove_from(obj)
|
|
365
|
+
# Update properties panel to reflect the removal
|
|
366
|
+
if obj is self.panel.objview.get_current_object():
|
|
367
|
+
self.panel.objprop.update_properties_from(obj)
|
|
368
|
+
# Update action states to reflect the removal
|
|
369
|
+
selected_groups = self.panel.objview.get_sel_groups()
|
|
370
|
+
selected_objects = self.panel.objview.get_sel_objects(include_groups=True)
|
|
371
|
+
self.selected_objects_changed(selected_groups, selected_objects)
|
|
372
|
+
# Refresh the plot to update the display
|
|
373
|
+
# Use the same refresh pattern as delete_results() method
|
|
374
|
+
self.panel.refresh_plot("selected", True, False)
|
|
375
|
+
|
|
376
|
+
@contextmanager
|
|
377
|
+
def new_category(self, category: ActionCategory) -> Generator[None, None, None]:
|
|
378
|
+
"""Context manager for creating a new menu.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
category: Action category
|
|
382
|
+
|
|
383
|
+
Yields:
|
|
384
|
+
None
|
|
385
|
+
"""
|
|
386
|
+
self.__category_in_progress = category
|
|
387
|
+
try:
|
|
388
|
+
yield
|
|
389
|
+
finally:
|
|
390
|
+
self.__category_in_progress = None
|
|
391
|
+
|
|
392
|
+
@contextmanager
|
|
393
|
+
def new_menu(
|
|
394
|
+
self,
|
|
395
|
+
title: str,
|
|
396
|
+
icon_name: str | None = None,
|
|
397
|
+
store_ref: str | None = None,
|
|
398
|
+
) -> Generator[None, None, None]:
|
|
399
|
+
"""Context manager for creating a new menu.
|
|
400
|
+
|
|
401
|
+
Args:
|
|
402
|
+
title: Menu title
|
|
403
|
+
icon_name: Menu icon name. Defaults to None.
|
|
404
|
+
store_ref: Optional attribute name to store menu reference.
|
|
405
|
+
Defaults to None.
|
|
406
|
+
|
|
407
|
+
Yields:
|
|
408
|
+
None
|
|
409
|
+
"""
|
|
410
|
+
# Create a unique key for this submenu level
|
|
411
|
+
parent_key = ""
|
|
412
|
+
if self.__submenu_stack:
|
|
413
|
+
parent_key = self.__submenu_stack[-1]["key"] + "/"
|
|
414
|
+
elif self.__category_in_progress:
|
|
415
|
+
parent_key = self.__category_in_progress.name + "/"
|
|
416
|
+
|
|
417
|
+
key = parent_key + title
|
|
418
|
+
is_new = key not in self.__submenus
|
|
419
|
+
|
|
420
|
+
if is_new:
|
|
421
|
+
self.__submenus[key] = menu = QW.QMenu(title)
|
|
422
|
+
if icon_name:
|
|
423
|
+
menu.setIcon(get_icon(icon_name))
|
|
424
|
+
# Store reference to menu if requested
|
|
425
|
+
if store_ref is not None:
|
|
426
|
+
setattr(self, store_ref, menu)
|
|
427
|
+
else:
|
|
428
|
+
menu = self.__submenus[key]
|
|
429
|
+
|
|
430
|
+
# Save current submenu state and push new submenu onto stack
|
|
431
|
+
submenu_state = {
|
|
432
|
+
"key": key,
|
|
433
|
+
"menu": menu,
|
|
434
|
+
"is_new": is_new,
|
|
435
|
+
"actions": [], # Actions for this submenu level
|
|
436
|
+
}
|
|
437
|
+
self.__submenu_stack.append(submenu_state)
|
|
438
|
+
self.__submenu_in_progress = True
|
|
439
|
+
|
|
440
|
+
try:
|
|
441
|
+
yield
|
|
442
|
+
finally:
|
|
443
|
+
# Pop the current submenu from stack
|
|
444
|
+
current_submenu = self.__submenu_stack.pop()
|
|
445
|
+
|
|
446
|
+
# Get actions for this specific submenu level
|
|
447
|
+
submenu_actions = current_submenu.get("actions", [])
|
|
448
|
+
|
|
449
|
+
# Also get any actions that were added to the generic SUBMENU category
|
|
450
|
+
# while this submenu was the active one
|
|
451
|
+
generic_submenu_actions = self.feature_actions.pop(
|
|
452
|
+
ActionCategory.SUBMENU, []
|
|
453
|
+
)
|
|
454
|
+
submenu_actions.extend(generic_submenu_actions)
|
|
455
|
+
|
|
456
|
+
add_actions(current_submenu["menu"], submenu_actions)
|
|
457
|
+
|
|
458
|
+
# Update submenu in progress status BEFORE adding to parent
|
|
459
|
+
self.__submenu_in_progress = len(self.__submenu_stack) > 0
|
|
460
|
+
|
|
461
|
+
if current_submenu["is_new"]:
|
|
462
|
+
# Add this submenu to its parent (either category or parent submenu)
|
|
463
|
+
if self.__submenu_stack:
|
|
464
|
+
# We're in a nested submenu, add to parent submenu's actions
|
|
465
|
+
parent_submenu = self.__submenu_stack[-1]
|
|
466
|
+
parent_submenu["actions"].append(current_submenu["menu"])
|
|
467
|
+
else:
|
|
468
|
+
# We're at the top level, add to category actions
|
|
469
|
+
# Force using the current category, not SUBMENU
|
|
470
|
+
self.add_to_action_list(
|
|
471
|
+
current_submenu["menu"], category=self.__category_in_progress
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# pylint: disable=too-many-positional-arguments
|
|
475
|
+
def new_action(
|
|
476
|
+
self,
|
|
477
|
+
title: str,
|
|
478
|
+
position: int | None = None,
|
|
479
|
+
separator: bool = False,
|
|
480
|
+
triggered: Callable | None = None,
|
|
481
|
+
toggled: Callable | None = None,
|
|
482
|
+
shortcut: QW.QShortcut | None = None,
|
|
483
|
+
icon_name: str | None = None,
|
|
484
|
+
tip: str | None = None,
|
|
485
|
+
select_condition: Callable | str | None = None,
|
|
486
|
+
context_menu_pos: int | None = None,
|
|
487
|
+
context_menu_sep: bool = False,
|
|
488
|
+
toolbar_pos: int | None = None,
|
|
489
|
+
toolbar_sep: bool = False,
|
|
490
|
+
toolbar_category: ActionCategory | None = None,
|
|
491
|
+
) -> QW.QAction:
|
|
492
|
+
"""Create new action and add it to list of actions.
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
title: action title
|
|
496
|
+
position: add action to menu at this position. Defaults to None.
|
|
497
|
+
separator: add separator before action in menu
|
|
498
|
+
(or after if pos is positive). Defaults to False.
|
|
499
|
+
triggered: triggered callback. Defaults to None.
|
|
500
|
+
toggled: toggled callback. Defaults to None.
|
|
501
|
+
shortcut: shortcut. Defaults to None.
|
|
502
|
+
icon_name: icon name. Defaults to None.
|
|
503
|
+
tip: tooltip. Defaults to None.
|
|
504
|
+
select_condition: selection condition. Defaults to None.
|
|
505
|
+
If str, must be the name of a method of SelectCond, i.e. one of
|
|
506
|
+
"always", "exactly_one", "exactly_one_group",
|
|
507
|
+
"at_least_one_group_or_one_object", "at_least_one",
|
|
508
|
+
"at_least_two", "with_roi".
|
|
509
|
+
context_menu_pos: add action to context menu at this position.
|
|
510
|
+
Defaults to None.
|
|
511
|
+
context_menu_sep: add separator before action in context menu
|
|
512
|
+
(or after if context_menu_pos is positive). Defaults to False.
|
|
513
|
+
toolbar_pos: add action to toolbar at this position. Defaults to None.
|
|
514
|
+
toolbar_sep: add separator before action in toolbar
|
|
515
|
+
(or after if toolbar_pos is positive). Defaults to False.
|
|
516
|
+
toolbar_category: toolbar category. Defaults to None.
|
|
517
|
+
If toolbar_pos is not None, this specifies the category of the toolbar.
|
|
518
|
+
If None, defaults to ActionCategory.VIEW_TOOLBAR if the current category
|
|
519
|
+
is ActionCategory.VIEW, else to ActionCategory.PANEL_TOOLBAR.
|
|
520
|
+
|
|
521
|
+
Returns:
|
|
522
|
+
New action
|
|
523
|
+
"""
|
|
524
|
+
if isinstance(select_condition, str):
|
|
525
|
+
assert select_condition in SelectCond.__dict__
|
|
526
|
+
select_condition = getattr(SelectCond, select_condition)
|
|
527
|
+
|
|
528
|
+
action = create_action(
|
|
529
|
+
parent=self.panel,
|
|
530
|
+
title=title,
|
|
531
|
+
triggered=triggered,
|
|
532
|
+
toggled=toggled,
|
|
533
|
+
shortcut=shortcut,
|
|
534
|
+
icon=get_icon(icon_name) if icon_name else None,
|
|
535
|
+
tip=tip,
|
|
536
|
+
context=QC.Qt.WidgetWithChildrenShortcut, # [1]
|
|
537
|
+
)
|
|
538
|
+
self.panel.addAction(action) # [1]
|
|
539
|
+
# [1] This is needed to make actions work with shortcuts for active panel,
|
|
540
|
+
# because some of the shortcuts are using the same keybindings for both panels.
|
|
541
|
+
# (Fixes #10)
|
|
542
|
+
|
|
543
|
+
self.add_action(action, select_condition)
|
|
544
|
+
self.add_to_action_list(action, None, position, separator)
|
|
545
|
+
if context_menu_pos is not None:
|
|
546
|
+
self.add_to_action_list(
|
|
547
|
+
action, ActionCategory.CONTEXT_MENU, context_menu_pos, context_menu_sep
|
|
548
|
+
)
|
|
549
|
+
if toolbar_pos is not None:
|
|
550
|
+
if toolbar_category is None:
|
|
551
|
+
if self.__category_in_progress is ActionCategory.VIEW:
|
|
552
|
+
toolbar_category = ActionCategory.VIEW_TOOLBAR
|
|
553
|
+
else:
|
|
554
|
+
toolbar_category = ActionCategory.PANEL_TOOLBAR
|
|
555
|
+
self.add_to_action_list(action, toolbar_category, toolbar_pos, toolbar_sep)
|
|
556
|
+
return action
|
|
557
|
+
|
|
558
|
+
def action_for(
|
|
559
|
+
self,
|
|
560
|
+
function_or_name: Callable | str,
|
|
561
|
+
position: int | None = None,
|
|
562
|
+
separator: bool = False,
|
|
563
|
+
context_menu_pos: int | None = None,
|
|
564
|
+
context_menu_sep: bool = False,
|
|
565
|
+
toolbar_pos: int | None = None,
|
|
566
|
+
toolbar_sep: bool = False,
|
|
567
|
+
toolbar_category: ActionCategory | None = None,
|
|
568
|
+
) -> QW.QAction:
|
|
569
|
+
"""Create action for a feature.
|
|
570
|
+
|
|
571
|
+
Args:
|
|
572
|
+
function_or_name: function or name of the feature
|
|
573
|
+
position: add action to menu at this position. Defaults to None.
|
|
574
|
+
separator: add separator before action in menu
|
|
575
|
+
context_menu_pos: add action to context menu at this position.
|
|
576
|
+
context_menu_pos: add action to context menu at this position.
|
|
577
|
+
Defaults to None.
|
|
578
|
+
context_menu_sep: add separator before action in context menu
|
|
579
|
+
(or after if context_menu_pos is positive). Defaults to False.
|
|
580
|
+
toolbar_pos: add action to toolbar at this position. Defaults to None.
|
|
581
|
+
toolbar_sep: add separator before action in toolbar
|
|
582
|
+
(or after if toolbar_pos is positive). Defaults to False.
|
|
583
|
+
toolbar_category: toolbar category. Defaults to None.
|
|
584
|
+
If toolbar_pos is not None, this specifies the category of the toolbar.
|
|
585
|
+
If None, defaults to ActionCategory.VIEW_TOOLBAR if the current category
|
|
586
|
+
is ActionCategory.VIEW, else to ActionCategory.PANEL_TOOLBAR.
|
|
587
|
+
|
|
588
|
+
Returns:
|
|
589
|
+
New action
|
|
590
|
+
"""
|
|
591
|
+
feature = self.panel.processor.get_feature(function_or_name)
|
|
592
|
+
if feature.pattern == "n_to_1":
|
|
593
|
+
condition = SelectCond.at_least_two
|
|
594
|
+
else:
|
|
595
|
+
condition = SelectCond.at_least_one
|
|
596
|
+
return self.new_action(
|
|
597
|
+
feature.action_title,
|
|
598
|
+
position=position,
|
|
599
|
+
separator=separator,
|
|
600
|
+
triggered=lambda: self.panel.processor.run_feature(feature.function),
|
|
601
|
+
select_condition=condition,
|
|
602
|
+
icon_name=feature.icon_name,
|
|
603
|
+
tip=feature.comment,
|
|
604
|
+
context_menu_pos=context_menu_pos,
|
|
605
|
+
context_menu_sep=context_menu_sep,
|
|
606
|
+
toolbar_pos=toolbar_pos,
|
|
607
|
+
toolbar_sep=toolbar_sep,
|
|
608
|
+
toolbar_category=toolbar_category,
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
def add_to_action_list(
|
|
612
|
+
self,
|
|
613
|
+
action: QW.QAction,
|
|
614
|
+
category: ActionCategory | None = None,
|
|
615
|
+
pos: int | None = None,
|
|
616
|
+
sep: bool = False,
|
|
617
|
+
) -> None:
|
|
618
|
+
"""Add action to list of actions.
|
|
619
|
+
|
|
620
|
+
Args:
|
|
621
|
+
action: action to add
|
|
622
|
+
category: action category. Defaults to None.
|
|
623
|
+
If None, action is added to the current category.
|
|
624
|
+
pos: add action to menu at this position. Defaults to None.
|
|
625
|
+
If None, action is added at the end of the list.
|
|
626
|
+
sep: add separator before action in menu
|
|
627
|
+
(or after if pos is positive). Defaults to False.
|
|
628
|
+
"""
|
|
629
|
+
if category is None:
|
|
630
|
+
if self.__submenu_in_progress and self.__submenu_stack:
|
|
631
|
+
# Add directly to the current submenu's action list
|
|
632
|
+
current_submenu = self.__submenu_stack[-1]
|
|
633
|
+
actionlist = current_submenu["actions"]
|
|
634
|
+
if pos is None:
|
|
635
|
+
pos = -1
|
|
636
|
+
add_separator_after = pos >= 0
|
|
637
|
+
if pos < 0:
|
|
638
|
+
pos = len(actionlist) + pos + 1
|
|
639
|
+
actionlist.insert(pos, action)
|
|
640
|
+
if sep:
|
|
641
|
+
if add_separator_after:
|
|
642
|
+
pos += 1
|
|
643
|
+
actionlist.insert(pos, None)
|
|
644
|
+
return
|
|
645
|
+
if self.__category_in_progress is not None:
|
|
646
|
+
category = self.__category_in_progress
|
|
647
|
+
else:
|
|
648
|
+
raise ValueError("No category specified")
|
|
649
|
+
if pos is None:
|
|
650
|
+
pos = -1
|
|
651
|
+
actionlist = self.feature_actions.setdefault(category, [])
|
|
652
|
+
add_separator_after = pos >= 0
|
|
653
|
+
if pos < 0:
|
|
654
|
+
pos = len(actionlist) + pos + 1
|
|
655
|
+
actionlist.insert(pos, action)
|
|
656
|
+
if sep:
|
|
657
|
+
if add_separator_after:
|
|
658
|
+
pos += 1
|
|
659
|
+
actionlist.insert(pos, None)
|
|
660
|
+
|
|
661
|
+
def add_action(
|
|
662
|
+
self, action: QW.QAction, select_condition: Callable | None = None
|
|
663
|
+
) -> None:
|
|
664
|
+
"""Add action to list of actions.
|
|
665
|
+
|
|
666
|
+
Args:
|
|
667
|
+
action: action to add
|
|
668
|
+
select_condition: condition to enable action. Defaults to None.
|
|
669
|
+
If None, action is enabled if at least one object is selected.
|
|
670
|
+
"""
|
|
671
|
+
if select_condition is None:
|
|
672
|
+
select_condition = SelectCond.at_least_one
|
|
673
|
+
self.__actions.setdefault(select_condition, []).append(action)
|
|
674
|
+
|
|
675
|
+
def selected_objects_changed(
|
|
676
|
+
self,
|
|
677
|
+
selected_groups: list[ObjectGroup],
|
|
678
|
+
selected_objects: list[SignalObj | ImageObj],
|
|
679
|
+
) -> None:
|
|
680
|
+
"""Update actions based on selected objects.
|
|
681
|
+
|
|
682
|
+
Args:
|
|
683
|
+
selected_groups: selected groups
|
|
684
|
+
selected_objects: selected objects
|
|
685
|
+
"""
|
|
686
|
+
for cond, actlist in self.__actions.items():
|
|
687
|
+
if cond is not None:
|
|
688
|
+
for act in actlist:
|
|
689
|
+
act.setEnabled(cond(selected_groups, selected_objects))
|
|
690
|
+
|
|
691
|
+
def create_all_actions(self):
|
|
692
|
+
"""Create all actions"""
|
|
693
|
+
self.create_first_actions()
|
|
694
|
+
self.create_last_actions()
|
|
695
|
+
|
|
696
|
+
# Connect ROI remove submenu signal after all actions are created
|
|
697
|
+
if self.roi_remove_submenu is not None:
|
|
698
|
+
self.roi_remove_submenu.aboutToShow.connect(
|
|
699
|
+
self.populate_roi_remove_submenu
|
|
700
|
+
)
|
|
701
|
+
# Add the submenu to the action management system with ROI condition
|
|
702
|
+
self.add_action(self.roi_remove_submenu, SelectCond.with_roi)
|
|
703
|
+
|
|
704
|
+
# Connect results delete submenu signal after all actions are created
|
|
705
|
+
if self.results_delete_submenu is not None:
|
|
706
|
+
self.results_delete_submenu.aboutToShow.connect(
|
|
707
|
+
self.populate_results_delete_submenu
|
|
708
|
+
)
|
|
709
|
+
# Add the submenu to the action management system
|
|
710
|
+
self.add_action(
|
|
711
|
+
self.results_delete_submenu,
|
|
712
|
+
SelectCond.at_least_one_group_or_one_object,
|
|
713
|
+
)
|
|
714
|
+
|
|
715
|
+
add_actions(
|
|
716
|
+
self.panel_toolbar, self.feature_actions.pop(ActionCategory.PANEL_TOOLBAR)
|
|
717
|
+
)
|
|
718
|
+
# For the view toolbar, we add the actions to the beginning of the toolbar:
|
|
719
|
+
before = self.view_toolbar.actions()[0]
|
|
720
|
+
for action in self.feature_actions.pop(ActionCategory.VIEW_TOOLBAR):
|
|
721
|
+
if action is None:
|
|
722
|
+
self.view_toolbar.insertSeparator(before)
|
|
723
|
+
else:
|
|
724
|
+
self.view_toolbar.insertAction(before, action)
|
|
725
|
+
self.view_toolbar.insertSeparator(before)
|
|
726
|
+
|
|
727
|
+
def create_first_actions(self):
|
|
728
|
+
"""Create actions that are added to the menus in the first place"""
|
|
729
|
+
# MARK: FILE
|
|
730
|
+
with self.new_category(ActionCategory.FILE):
|
|
731
|
+
self.new_action(
|
|
732
|
+
_("Open %s...") % self.OBJECT_STR,
|
|
733
|
+
# Icon name is 'fileopen_sig.svg' or 'fileopen_ima.svg':
|
|
734
|
+
icon_name=f"fileopen_{self.object_suffix}.svg",
|
|
735
|
+
tip=_("Open one or more %s files") % self.OBJECT_STR,
|
|
736
|
+
triggered=self.panel.load_from_files,
|
|
737
|
+
shortcut=QG.QKeySequence(QG.QKeySequence.Open),
|
|
738
|
+
select_condition=SelectCond.always,
|
|
739
|
+
toolbar_pos=-1,
|
|
740
|
+
)
|
|
741
|
+
self.new_action(
|
|
742
|
+
_("Open from directory..."),
|
|
743
|
+
icon_name="fileopen_directory.svg",
|
|
744
|
+
tip=_("Open all %s files from directory") % self.OBJECT_STR,
|
|
745
|
+
triggered=self.panel.load_from_directory,
|
|
746
|
+
select_condition=SelectCond.always,
|
|
747
|
+
toolbar_pos=-1,
|
|
748
|
+
)
|
|
749
|
+
self.new_action(
|
|
750
|
+
_("Save %s...") % self.OBJECT_STR,
|
|
751
|
+
# Icon name is 'filesave_sig.svg' or 'filesave_ima.svg'
|
|
752
|
+
icon_name=f"filesave_{self.object_suffix}.svg",
|
|
753
|
+
tip=_("Save selected %s") % self.OBJECT_STR_PLURAL,
|
|
754
|
+
triggered=self.panel.save_to_files,
|
|
755
|
+
shortcut=QG.QKeySequence(QG.QKeySequence.Save),
|
|
756
|
+
select_condition=SelectCond.at_least_one,
|
|
757
|
+
context_menu_pos=-1,
|
|
758
|
+
toolbar_pos=-1,
|
|
759
|
+
)
|
|
760
|
+
self.new_action(
|
|
761
|
+
_("Save to directory..."),
|
|
762
|
+
icon_name="save_to_directory.svg",
|
|
763
|
+
tip=_("Save selected %s using a filename pattern")
|
|
764
|
+
% self.OBJECT_STR_PLURAL,
|
|
765
|
+
triggered=self.panel.save_to_directory,
|
|
766
|
+
select_condition=SelectCond.at_least_two,
|
|
767
|
+
context_menu_pos=-1,
|
|
768
|
+
toolbar_pos=-1,
|
|
769
|
+
)
|
|
770
|
+
self.new_action(
|
|
771
|
+
_("Import text file..."),
|
|
772
|
+
icon_name="import_text.svg",
|
|
773
|
+
triggered=self.panel.exec_import_wizard,
|
|
774
|
+
select_condition=SelectCond.always,
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
# MARK: EDIT
|
|
778
|
+
with self.new_category(ActionCategory.EDIT):
|
|
779
|
+
self.new_action(
|
|
780
|
+
_("Recompute"),
|
|
781
|
+
icon_name="recompute.svg",
|
|
782
|
+
shortcut="Ctrl+R",
|
|
783
|
+
tip=_("Recompute selected %s with its processing parameters")
|
|
784
|
+
% self.OBJECT_STR,
|
|
785
|
+
triggered=self.panel.recompute_processing,
|
|
786
|
+
select_condition=SelectCond.at_least_one_group_or_one_object,
|
|
787
|
+
context_menu_pos=-1,
|
|
788
|
+
toolbar_pos=-1,
|
|
789
|
+
)
|
|
790
|
+
self.new_action(
|
|
791
|
+
_("Select source objects"),
|
|
792
|
+
icon_name="goto_source.svg",
|
|
793
|
+
tip=_("Select source object(s) used to create the selected %s")
|
|
794
|
+
% self.OBJECT_STR,
|
|
795
|
+
triggered=self.panel.select_source_objects,
|
|
796
|
+
select_condition=SelectCond.exactly_one,
|
|
797
|
+
context_menu_pos=-1,
|
|
798
|
+
toolbar_pos=-1,
|
|
799
|
+
)
|
|
800
|
+
self.new_action(
|
|
801
|
+
_("Rename"),
|
|
802
|
+
icon_name="rename.svg",
|
|
803
|
+
shortcut="F2",
|
|
804
|
+
tip=_("Edit title of selected %s or group") % self.OBJECT_STR,
|
|
805
|
+
triggered=self.panel.rename_selected_object_or_group,
|
|
806
|
+
select_condition=SelectCond.exactly_one_group_or_one_object,
|
|
807
|
+
context_menu_pos=-1,
|
|
808
|
+
separator=True,
|
|
809
|
+
)
|
|
810
|
+
self.new_action(
|
|
811
|
+
_("New group..."),
|
|
812
|
+
icon_name="new_group.svg",
|
|
813
|
+
tip=_("Create a new group"),
|
|
814
|
+
triggered=self.panel.new_group,
|
|
815
|
+
select_condition=SelectCond.always,
|
|
816
|
+
context_menu_pos=-1,
|
|
817
|
+
)
|
|
818
|
+
self.new_action(
|
|
819
|
+
_("Move up"),
|
|
820
|
+
icon_name="move_up.svg",
|
|
821
|
+
tip=_("Move up selection (groups or objects)"),
|
|
822
|
+
triggered=self.panel.objview.move_up,
|
|
823
|
+
select_condition=SelectCond.at_least_one_group_or_one_object,
|
|
824
|
+
context_menu_pos=-1,
|
|
825
|
+
toolbar_pos=-1,
|
|
826
|
+
)
|
|
827
|
+
self.new_action(
|
|
828
|
+
_("Move down"),
|
|
829
|
+
icon_name="move_down.svg",
|
|
830
|
+
tip=_("Move down selection (groups or objects)"),
|
|
831
|
+
triggered=self.panel.objview.move_down,
|
|
832
|
+
select_condition=SelectCond.at_least_one_group_or_one_object,
|
|
833
|
+
context_menu_pos=-1,
|
|
834
|
+
toolbar_pos=-1,
|
|
835
|
+
)
|
|
836
|
+
self.new_action(
|
|
837
|
+
_("Duplicate"),
|
|
838
|
+
icon_name="duplicate.svg",
|
|
839
|
+
tip=_("Duplicate selected %s") % self.OBJECT_STR,
|
|
840
|
+
separator=True,
|
|
841
|
+
triggered=self.panel.duplicate_object,
|
|
842
|
+
shortcut=QG.QKeySequence(QG.QKeySequence.Copy),
|
|
843
|
+
select_condition=SelectCond.at_least_one_group_or_one_object,
|
|
844
|
+
context_menu_pos=-1,
|
|
845
|
+
toolbar_pos=-1,
|
|
846
|
+
)
|
|
847
|
+
self.new_action(
|
|
848
|
+
_("Remove"),
|
|
849
|
+
icon_name="delete.svg",
|
|
850
|
+
tip=_("Remove selected %s") % self.OBJECT_STR,
|
|
851
|
+
triggered=self.panel.remove_object,
|
|
852
|
+
shortcut=QG.QKeySequence(QG.QKeySequence.Delete),
|
|
853
|
+
select_condition=SelectCond.at_least_one_group_or_one_object,
|
|
854
|
+
context_menu_pos=-1,
|
|
855
|
+
toolbar_pos=-1,
|
|
856
|
+
)
|
|
857
|
+
self.new_action(
|
|
858
|
+
_("Delete all"),
|
|
859
|
+
select_condition=SelectCond.always,
|
|
860
|
+
shortcut="Shift+Ctrl+Suppr",
|
|
861
|
+
tip=_("Delete all groups and objects"),
|
|
862
|
+
icon_name="delete_all.svg",
|
|
863
|
+
triggered=self.panel.delete_all_objects,
|
|
864
|
+
toolbar_pos=-1,
|
|
865
|
+
)
|
|
866
|
+
with self.new_menu(
|
|
867
|
+
_("Metadata"), icon_name="metadata.svg", store_ref="metadata_submenu"
|
|
868
|
+
):
|
|
869
|
+
self.new_action(
|
|
870
|
+
_("Copy metadata"),
|
|
871
|
+
icon_name="metadata_copy.svg",
|
|
872
|
+
tip=_("Copy metadata from selected %s") % self.OBJECT_STR,
|
|
873
|
+
triggered=self.panel.copy_metadata,
|
|
874
|
+
select_condition=SelectCond.exactly_one,
|
|
875
|
+
toolbar_pos=-1,
|
|
876
|
+
)
|
|
877
|
+
self.new_action(
|
|
878
|
+
_("Paste metadata"),
|
|
879
|
+
icon_name="metadata_paste.svg",
|
|
880
|
+
tip=_("Paste metadata into selected %s") % self.OBJECT_STR,
|
|
881
|
+
triggered=self.panel.paste_metadata,
|
|
882
|
+
select_condition=self.has_metadata_in_clipboard,
|
|
883
|
+
toolbar_pos=-1,
|
|
884
|
+
)
|
|
885
|
+
self.new_action(
|
|
886
|
+
_("Add metadata") + "...",
|
|
887
|
+
separator=True,
|
|
888
|
+
icon_name="metadata_add.svg",
|
|
889
|
+
tip=_("Add a metadata item to selected %s") % self.OBJECT_STR,
|
|
890
|
+
triggered=self.panel.add_metadata,
|
|
891
|
+
select_condition=SelectCond.at_least_one,
|
|
892
|
+
toolbar_pos=-1,
|
|
893
|
+
)
|
|
894
|
+
self.new_action(
|
|
895
|
+
_("Import metadata") + "...",
|
|
896
|
+
icon_name="metadata_import.svg",
|
|
897
|
+
tip=_("Import metadata into %s") % self.OBJECT_STR,
|
|
898
|
+
triggered=self.panel.import_metadata_from_file,
|
|
899
|
+
select_condition=SelectCond.exactly_one,
|
|
900
|
+
toolbar_pos=-1,
|
|
901
|
+
)
|
|
902
|
+
self.new_action(
|
|
903
|
+
_("Export metadata") + "...",
|
|
904
|
+
icon_name="metadata_export.svg",
|
|
905
|
+
tip=_("Export selected %s metadata") % self.OBJECT_STR,
|
|
906
|
+
triggered=self.panel.export_metadata_from_file,
|
|
907
|
+
select_condition=SelectCond.exactly_one,
|
|
908
|
+
toolbar_pos=-1,
|
|
909
|
+
)
|
|
910
|
+
self.new_action(
|
|
911
|
+
_("Delete object metadata"),
|
|
912
|
+
separator=True,
|
|
913
|
+
icon_name="metadata_delete.svg",
|
|
914
|
+
tip=_("Delete all that is contained in object metadata"),
|
|
915
|
+
triggered=self.panel.delete_metadata,
|
|
916
|
+
toolbar_pos=-1,
|
|
917
|
+
)
|
|
918
|
+
with self.new_menu(
|
|
919
|
+
_("Annotations"),
|
|
920
|
+
icon_name="annotations.svg",
|
|
921
|
+
store_ref="annotations_submenu",
|
|
922
|
+
):
|
|
923
|
+
self.new_action(
|
|
924
|
+
_("Copy annotations"),
|
|
925
|
+
icon_name="annotations_copy.svg",
|
|
926
|
+
tip=_("Copy annotations from selected %s") % self.OBJECT_STR,
|
|
927
|
+
triggered=self.panel.copy_annotations,
|
|
928
|
+
select_condition=SelectCond.exactly_one_with_annotations,
|
|
929
|
+
)
|
|
930
|
+
self.new_action(
|
|
931
|
+
_("Paste annotations"),
|
|
932
|
+
icon_name="annotations_paste.svg",
|
|
933
|
+
tip=_("Paste annotations into selected %s") % self.OBJECT_STR,
|
|
934
|
+
triggered=self.panel.paste_annotations,
|
|
935
|
+
select_condition=self.has_annotations_in_clipboard,
|
|
936
|
+
)
|
|
937
|
+
self.new_action(
|
|
938
|
+
_("Edit annotations") + "...",
|
|
939
|
+
separator=True,
|
|
940
|
+
icon_name="annotations_edit.svg",
|
|
941
|
+
tip=_("Edit annotations of selected %s") % self.OBJECT_STR,
|
|
942
|
+
triggered=lambda: self.panel.open_separate_view(
|
|
943
|
+
edit_annotations=True
|
|
944
|
+
),
|
|
945
|
+
select_condition=SelectCond.exactly_one,
|
|
946
|
+
)
|
|
947
|
+
self.new_action(
|
|
948
|
+
_("Import annotations") + "...",
|
|
949
|
+
icon_name="annotations_import.svg",
|
|
950
|
+
tip=_("Import annotations into %s") % self.OBJECT_STR,
|
|
951
|
+
triggered=self.panel.import_annotations_from_file,
|
|
952
|
+
select_condition=SelectCond.exactly_one,
|
|
953
|
+
)
|
|
954
|
+
self.new_action(
|
|
955
|
+
_("Export annotations") + "...",
|
|
956
|
+
icon_name="annotations_export.svg",
|
|
957
|
+
tip=_("Export selected %s annotations") % self.OBJECT_STR,
|
|
958
|
+
triggered=self.panel.export_annotations_from_file,
|
|
959
|
+
select_condition=SelectCond.exactly_one_with_annotations,
|
|
960
|
+
)
|
|
961
|
+
self.new_action(
|
|
962
|
+
_("Delete annotations"),
|
|
963
|
+
separator=True,
|
|
964
|
+
icon_name="annotations_delete.svg",
|
|
965
|
+
tip=_("Delete all annotations from selected %s") % self.OBJECT_STR,
|
|
966
|
+
triggered=self.panel.delete_annotations,
|
|
967
|
+
select_condition=SelectCond.with_annotations,
|
|
968
|
+
)
|
|
969
|
+
self.new_action(
|
|
970
|
+
_("Insert object title as annotation label"),
|
|
971
|
+
separator=True,
|
|
972
|
+
triggered=lambda: self.panel.add_label_with_title(ignore_msg=False),
|
|
973
|
+
tip=_(
|
|
974
|
+
"Add the selected object's title as a label to the plot annotations"
|
|
975
|
+
),
|
|
976
|
+
)
|
|
977
|
+
self.new_action(
|
|
978
|
+
_("Copy titles to clipboard"),
|
|
979
|
+
icon_name="copy_titles.svg",
|
|
980
|
+
tip=_("Copy titles of selected objects to clipboard"),
|
|
981
|
+
triggered=self.panel.copy_titles_to_clipboard,
|
|
982
|
+
)
|
|
983
|
+
|
|
984
|
+
# MARK: ROI
|
|
985
|
+
with self.new_category(ActionCategory.ROI):
|
|
986
|
+
self.new_action(
|
|
987
|
+
_("Edit graphically") + "...",
|
|
988
|
+
triggered=self.panel.processor.edit_roi_graphically,
|
|
989
|
+
icon_name="roi.svg",
|
|
990
|
+
context_menu_pos=-1,
|
|
991
|
+
context_menu_sep=True,
|
|
992
|
+
toolbar_pos=-1,
|
|
993
|
+
toolbar_category=ActionCategory.VIEW_TOOLBAR,
|
|
994
|
+
tip=_("Edit regions of interest graphically"),
|
|
995
|
+
)
|
|
996
|
+
self.new_action(
|
|
997
|
+
_("Edit numerically") + "...",
|
|
998
|
+
triggered=self.panel.processor.edit_roi_numerically,
|
|
999
|
+
select_condition=SelectCond.exactly_one_with_roi,
|
|
1000
|
+
tip=_("Edit regions of interest numerically"),
|
|
1001
|
+
)
|
|
1002
|
+
|
|
1003
|
+
# MARK: VIEW
|
|
1004
|
+
with self.new_category(ActionCategory.VIEW):
|
|
1005
|
+
self.new_action(
|
|
1006
|
+
_("View in a new window") + "...",
|
|
1007
|
+
icon_name="new_window.svg",
|
|
1008
|
+
tip=_("View selected %s in a new window") % self.OBJECT_STR,
|
|
1009
|
+
triggered=self.panel.open_separate_view,
|
|
1010
|
+
context_menu_pos=0,
|
|
1011
|
+
context_menu_sep=True,
|
|
1012
|
+
toolbar_pos=0,
|
|
1013
|
+
)
|
|
1014
|
+
|
|
1015
|
+
# MARK: OPERATION
|
|
1016
|
+
with self.new_category(ActionCategory.OPERATION):
|
|
1017
|
+
self.action_for("arithmetic")
|
|
1018
|
+
with self.new_menu(_("Constant Operations"), icon_name="constant.svg"):
|
|
1019
|
+
self.action_for("addition_constant")
|
|
1020
|
+
self.action_for("difference_constant")
|
|
1021
|
+
self.action_for("product_constant")
|
|
1022
|
+
self.action_for("division_constant")
|
|
1023
|
+
self.action_for("addition")
|
|
1024
|
+
self.action_for("difference")
|
|
1025
|
+
self.action_for("product")
|
|
1026
|
+
self.action_for("division")
|
|
1027
|
+
self.action_for("inverse", separator=True)
|
|
1028
|
+
self.action_for("exp")
|
|
1029
|
+
self.action_for("log10")
|
|
1030
|
+
|
|
1031
|
+
# MARK: PROCESSING
|
|
1032
|
+
with self.new_category(ActionCategory.PROCESSING):
|
|
1033
|
+
with self.new_menu(
|
|
1034
|
+
_("Axis transformation"), icon_name="axis_transform.svg"
|
|
1035
|
+
):
|
|
1036
|
+
self.action_for("calibration")
|
|
1037
|
+
with self.new_menu(_("Level adjustment"), icon_name="level_adjustment.svg"):
|
|
1038
|
+
self.action_for("normalize")
|
|
1039
|
+
self.action_for("clip")
|
|
1040
|
+
self.new_action(
|
|
1041
|
+
_("Offset correction"),
|
|
1042
|
+
triggered=self.panel.processor.compute_offset_correction,
|
|
1043
|
+
icon_name="offset_correction.svg",
|
|
1044
|
+
tip=_("Evaluate and subtract the offset value from the data"),
|
|
1045
|
+
)
|
|
1046
|
+
with self.new_menu(_("Noise addition"), icon_name="noise_addition.svg"):
|
|
1047
|
+
self.action_for("add_gaussian_noise")
|
|
1048
|
+
self.action_for("add_poisson_noise")
|
|
1049
|
+
self.action_for("add_uniform_noise")
|
|
1050
|
+
with self.new_menu(_("Noise reduction"), icon_name="noise_reduction.svg"):
|
|
1051
|
+
self.action_for("gaussian_filter")
|
|
1052
|
+
self.action_for("moving_average")
|
|
1053
|
+
self.action_for("moving_median")
|
|
1054
|
+
self.action_for("wiener")
|
|
1055
|
+
with self.new_menu(_("Fourier analysis"), icon_name="fourier.svg"):
|
|
1056
|
+
self.action_for("zero_padding")
|
|
1057
|
+
self.action_for("fft")
|
|
1058
|
+
self.action_for("ifft")
|
|
1059
|
+
self.action_for("magnitude_spectrum")
|
|
1060
|
+
self.action_for("phase_spectrum")
|
|
1061
|
+
self.action_for("psd")
|
|
1062
|
+
|
|
1063
|
+
# MARK: ANALYSIS
|
|
1064
|
+
with self.new_category(ActionCategory.ANALYSIS):
|
|
1065
|
+
self.action_for("stats", context_menu_pos=-1, context_menu_sep=True)
|
|
1066
|
+
self.action_for("histogram", context_menu_pos=-1)
|
|
1067
|
+
|
|
1068
|
+
def create_last_actions(self):
|
|
1069
|
+
"""Create actions that are added to the menus in the end"""
|
|
1070
|
+
# MARK: ROI
|
|
1071
|
+
with self.new_category(ActionCategory.ROI):
|
|
1072
|
+
self.new_action(
|
|
1073
|
+
_("Extract") + "...",
|
|
1074
|
+
triggered=self.panel.processor.compute_roi_extraction,
|
|
1075
|
+
# Icon name is 'roi_sig.svg' or 'roi_ima.svg':
|
|
1076
|
+
icon_name=f"roi_{self.object_suffix}.svg",
|
|
1077
|
+
separator=True,
|
|
1078
|
+
)
|
|
1079
|
+
self.new_action(
|
|
1080
|
+
_("Copy"),
|
|
1081
|
+
separator=True,
|
|
1082
|
+
icon_name="roi_copy.svg",
|
|
1083
|
+
tip=_("Copy regions of interest from selected %s") % self.OBJECT_STR,
|
|
1084
|
+
triggered=self.panel.copy_roi,
|
|
1085
|
+
select_condition=SelectCond.exactly_one_with_roi,
|
|
1086
|
+
toolbar_pos=-1,
|
|
1087
|
+
)
|
|
1088
|
+
self.new_action(
|
|
1089
|
+
_("Paste"),
|
|
1090
|
+
icon_name="roi_paste.svg",
|
|
1091
|
+
tip=_("Paste regions of interest into selected %s") % self.OBJECT_STR,
|
|
1092
|
+
triggered=self.panel.paste_roi,
|
|
1093
|
+
toolbar_pos=-1,
|
|
1094
|
+
)
|
|
1095
|
+
self.new_action(
|
|
1096
|
+
_("Import") + "...",
|
|
1097
|
+
icon_name="roi_import.svg",
|
|
1098
|
+
tip=_("Import regions of interest into %s") % self.OBJECT_STR,
|
|
1099
|
+
triggered=self.panel.import_roi_from_file,
|
|
1100
|
+
select_condition=SelectCond.exactly_one,
|
|
1101
|
+
toolbar_pos=-1,
|
|
1102
|
+
)
|
|
1103
|
+
self.new_action(
|
|
1104
|
+
_("Export") + "...",
|
|
1105
|
+
icon_name="roi_export.svg",
|
|
1106
|
+
tip=_("Export selected %s regions of interest") % self.OBJECT_STR,
|
|
1107
|
+
triggered=self.panel.export_roi_to_file,
|
|
1108
|
+
select_condition=SelectCond.exactly_one_with_roi,
|
|
1109
|
+
toolbar_pos=-1,
|
|
1110
|
+
)
|
|
1111
|
+
|
|
1112
|
+
# Create dynamic "Remove" submenu
|
|
1113
|
+
with self.new_menu(_("Remove"), icon_name="roi_delete.svg"):
|
|
1114
|
+
# Store reference to the submenu for dynamic population
|
|
1115
|
+
if self.__submenu_stack:
|
|
1116
|
+
current_submenu = self.__submenu_stack[-1]
|
|
1117
|
+
self.roi_remove_submenu = current_submenu["menu"]
|
|
1118
|
+
|
|
1119
|
+
# MARK: OPERATION
|
|
1120
|
+
with self.new_category(ActionCategory.OPERATION):
|
|
1121
|
+
self.action_for("absolute", separator=True)
|
|
1122
|
+
self.action_for("phase")
|
|
1123
|
+
self.action_for("complex_from_magnitude_phase")
|
|
1124
|
+
self.action_for("real", separator=True)
|
|
1125
|
+
self.action_for("imag")
|
|
1126
|
+
self.action_for("complex_from_real_imag")
|
|
1127
|
+
self.action_for("astype", separator=True)
|
|
1128
|
+
self.action_for("average", separator=True)
|
|
1129
|
+
self.action_for("standard_deviation")
|
|
1130
|
+
self.action_for("quadratic_difference")
|
|
1131
|
+
self.action_for("convolution", separator=True)
|
|
1132
|
+
self.action_for("deconvolution")
|
|
1133
|
+
|
|
1134
|
+
# MARK: ANALYSIS
|
|
1135
|
+
with self.new_category(ActionCategory.ANALYSIS):
|
|
1136
|
+
self.new_action(
|
|
1137
|
+
_("Show results") + "...",
|
|
1138
|
+
triggered=self.panel.show_results,
|
|
1139
|
+
icon_name="show_results.svg",
|
|
1140
|
+
separator=True,
|
|
1141
|
+
select_condition=SelectCond.at_least_one_group_or_one_object,
|
|
1142
|
+
)
|
|
1143
|
+
self.show_label_action = self.new_action(
|
|
1144
|
+
_("Results label"),
|
|
1145
|
+
toggled=self.panel.toggle_result_label_visibility,
|
|
1146
|
+
tip=_("Show or hide the merged result label on the plot"),
|
|
1147
|
+
select_condition=SelectCond.with_results,
|
|
1148
|
+
)
|
|
1149
|
+
self.show_label_action.setCheckable(True)
|
|
1150
|
+
self.show_label_action.setChecked(Conf.view.show_result_label.get())
|
|
1151
|
+
self.new_action(
|
|
1152
|
+
_("Plot results") + "...",
|
|
1153
|
+
triggered=self.panel.plot_results,
|
|
1154
|
+
icon_name="plot_results.svg",
|
|
1155
|
+
select_condition=SelectCond.at_least_one_group_or_one_object,
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1158
|
+
# Create dynamic "Delete results" submenu
|
|
1159
|
+
with self.new_menu(_("Delete results"), icon_name="delete_results.svg"):
|
|
1160
|
+
# Store reference to the submenu for dynamic population
|
|
1161
|
+
if self.__submenu_stack:
|
|
1162
|
+
current_submenu = self.__submenu_stack[-1]
|
|
1163
|
+
self.results_delete_submenu = current_submenu["menu"]
|
|
1164
|
+
|
|
1165
|
+
# MARK: VIEW
|
|
1166
|
+
with self.new_category(ActionCategory.VIEW):
|
|
1167
|
+
self.new_action(
|
|
1168
|
+
_("Edit annotations") + "...",
|
|
1169
|
+
icon_name="annotations.svg",
|
|
1170
|
+
tip=_("Edit annotations of selected %s") % self.OBJECT_STR,
|
|
1171
|
+
triggered=lambda: self.panel.open_separate_view(edit_annotations=True),
|
|
1172
|
+
context_menu_pos=1,
|
|
1173
|
+
toolbar_pos=-1,
|
|
1174
|
+
)
|
|
1175
|
+
main = self.panel.mainwindow
|
|
1176
|
+
for cat in (ActionCategory.VIEW, ActionCategory.VIEW_TOOLBAR):
|
|
1177
|
+
for act in (main.autorefresh_action, main.showfirstonly_action):
|
|
1178
|
+
self.add_to_action_list(act, cat, -1)
|
|
1179
|
+
self.new_action(
|
|
1180
|
+
_("Refresh manually"),
|
|
1181
|
+
icon_name="refresh-manual.svg",
|
|
1182
|
+
tip=_("Refresh plot, even if auto-refresh is enabled"),
|
|
1183
|
+
shortcut=QG.QKeySequence(QG.QKeySequence.Refresh),
|
|
1184
|
+
triggered=self.panel.manual_refresh,
|
|
1185
|
+
select_condition=SelectCond.always,
|
|
1186
|
+
toolbar_pos=-1,
|
|
1187
|
+
)
|
|
1188
|
+
for cat in (ActionCategory.VIEW, ActionCategory.VIEW_TOOLBAR):
|
|
1189
|
+
self.add_to_action_list(main.showlabel_action, cat, -1)
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
class SignalActionHandler(BaseActionHandler):
|
|
1193
|
+
"""Object handling signal panel GUI interactions: actions, menus, ..."""
|
|
1194
|
+
|
|
1195
|
+
OBJECT_STR = _("signal")
|
|
1196
|
+
OBJECT_STR_PLURAL = _("signals")
|
|
1197
|
+
|
|
1198
|
+
def create_first_actions(self):
|
|
1199
|
+
"""Create actions that are added to the menus in the first place"""
|
|
1200
|
+
super().create_first_actions()
|
|
1201
|
+
|
|
1202
|
+
# MARK: CREATE
|
|
1203
|
+
with self.new_category(ActionCategory.CREATE):
|
|
1204
|
+
for label, pclass, icon_name, separator in (
|
|
1205
|
+
(_("Zero"), sio.ZeroParam, "1d-zero.svg", False),
|
|
1206
|
+
(
|
|
1207
|
+
_("Normal distribution"),
|
|
1208
|
+
sio.NormalDistribution1DParam,
|
|
1209
|
+
"1d-normal.svg",
|
|
1210
|
+
False,
|
|
1211
|
+
),
|
|
1212
|
+
(
|
|
1213
|
+
_("Poisson distribution"),
|
|
1214
|
+
sio.PoissonDistribution1DParam,
|
|
1215
|
+
"1d-poisson.svg",
|
|
1216
|
+
False,
|
|
1217
|
+
),
|
|
1218
|
+
(
|
|
1219
|
+
_("Uniform distribution"),
|
|
1220
|
+
sio.UniformDistribution1DParam,
|
|
1221
|
+
"1d-uniform.svg",
|
|
1222
|
+
False,
|
|
1223
|
+
),
|
|
1224
|
+
(_("Gaussian"), sio.GaussParam, "gaussian.svg", True),
|
|
1225
|
+
(_("Lorentzian"), sio.LorentzParam, "lorentzian.svg", False),
|
|
1226
|
+
(_("Voigt"), sio.VoigtParam, "voigt.svg", False),
|
|
1227
|
+
(_("Blackbody (Planck's law)"), sio.PlanckParam, "planck.svg", False),
|
|
1228
|
+
(_("Sine"), sio.SineParam, "sine.svg", True),
|
|
1229
|
+
(_("Cosine"), sio.CosineParam, "cosine.svg", False),
|
|
1230
|
+
(_("Sawtooth"), sio.SawtoothParam, "sawtooth.svg", False),
|
|
1231
|
+
(_("Triangle"), sio.TriangleParam, "triangle.svg", False),
|
|
1232
|
+
(_("Square"), sio.SquareParam, "square.svg", False),
|
|
1233
|
+
(_("Cardinal sine"), sio.SincParam, "sinc.svg", False),
|
|
1234
|
+
(_("Linear chirp"), sio.LinearChirpParam, "linear_chirp.svg", False),
|
|
1235
|
+
(_("Step"), sio.StepParam, "step.svg", True),
|
|
1236
|
+
(_("Exponential"), sio.ExponentialParam, "exponential.svg", False),
|
|
1237
|
+
(_("Logistic"), sio.LogisticParam, "logistic.svg", False),
|
|
1238
|
+
(_("Pulse"), sio.PulseParam, "pulse.svg", False),
|
|
1239
|
+
(_("Step pulse"), sio.StepPulseParam, "step_pulse.svg", False),
|
|
1240
|
+
(_("Square pulse"), sio.SquarePulseParam, "square_pulse.svg", False),
|
|
1241
|
+
(_("Polynomial"), sio.PolyParam, "polynomial.svg", True),
|
|
1242
|
+
(_("Custom"), newobject.CustomSignalParam, None, False),
|
|
1243
|
+
):
|
|
1244
|
+
self.new_action(
|
|
1245
|
+
label,
|
|
1246
|
+
tip=_("Create new %s") % label,
|
|
1247
|
+
triggered=lambda pclass=pclass: self.panel.new_object(pclass()),
|
|
1248
|
+
icon_name=icon_name,
|
|
1249
|
+
select_condition=SelectCond.always,
|
|
1250
|
+
separator=separator,
|
|
1251
|
+
)
|
|
1252
|
+
|
|
1253
|
+
# MARK: OPERATION
|
|
1254
|
+
with self.new_category(ActionCategory.OPERATION):
|
|
1255
|
+
self.action_for("power", separator=True)
|
|
1256
|
+
self.action_for("sqrt")
|
|
1257
|
+
|
|
1258
|
+
def cra_fit(title, fitdlgfunc, tip: str | None = None):
|
|
1259
|
+
"""Create curve fitting action"""
|
|
1260
|
+
return self.new_action(
|
|
1261
|
+
title,
|
|
1262
|
+
triggered=lambda: self.panel.processor.compute_fit(title, fitdlgfunc),
|
|
1263
|
+
icon_name=fitdlgfunc.__name__ + ".svg",
|
|
1264
|
+
tip=tip,
|
|
1265
|
+
)
|
|
1266
|
+
|
|
1267
|
+
# MARK: PROCESSING
|
|
1268
|
+
with self.new_category(ActionCategory.PROCESSING):
|
|
1269
|
+
with self.new_menu(_("Axis transformation")):
|
|
1270
|
+
self.action_for("transpose")
|
|
1271
|
+
self.action_for("reverse_x")
|
|
1272
|
+
self.action_for("to_cartesian")
|
|
1273
|
+
self.action_for("to_polar")
|
|
1274
|
+
with self.new_menu(_("Frequency filters"), icon_name="highpass.svg"):
|
|
1275
|
+
self.action_for("lowpass")
|
|
1276
|
+
self.action_for("highpass")
|
|
1277
|
+
self.action_for("bandpass")
|
|
1278
|
+
self.action_for("bandstop")
|
|
1279
|
+
with self.new_menu(_("Fitting"), icon_name="exponential_fit.svg"):
|
|
1280
|
+
with self.new_menu(
|
|
1281
|
+
_("Interactive fitting"), icon_name="interactive_fit.svg"
|
|
1282
|
+
):
|
|
1283
|
+
cra_fit(_("Linear fit"), fitdialog.linear_fit)
|
|
1284
|
+
self.new_action(
|
|
1285
|
+
_("Polynomial fit"),
|
|
1286
|
+
triggered=self.panel.processor.compute_polyfit,
|
|
1287
|
+
icon_name="polynomial_fit.svg",
|
|
1288
|
+
)
|
|
1289
|
+
cra_fit(_("Gaussian fit"), fitdialog.gaussian_fit)
|
|
1290
|
+
cra_fit(_("Lorentzian fit"), fitdialog.lorentzian_fit)
|
|
1291
|
+
cra_fit(_("Voigt fit"), fitdialog.voigt_fit)
|
|
1292
|
+
self.new_action(
|
|
1293
|
+
_("Multi-Gaussian fit"),
|
|
1294
|
+
triggered=self.panel.processor.compute_multigaussianfit,
|
|
1295
|
+
icon_name="multigaussian_fit.svg",
|
|
1296
|
+
)
|
|
1297
|
+
self.new_action(
|
|
1298
|
+
_("Multi-Lorentzian fit"),
|
|
1299
|
+
triggered=self.panel.processor.compute_multilorentzianfit,
|
|
1300
|
+
icon_name="multilorentzian_fit.svg",
|
|
1301
|
+
)
|
|
1302
|
+
cra_fit(
|
|
1303
|
+
_("Planckian fit"),
|
|
1304
|
+
fitdialog.planckian_fit,
|
|
1305
|
+
tip=_("Planckian (blackbody radiation) fitting"),
|
|
1306
|
+
)
|
|
1307
|
+
cra_fit(
|
|
1308
|
+
_("Two half-Gaussian fit"),
|
|
1309
|
+
fitdialog.twohalfgaussian_fit,
|
|
1310
|
+
tip=_("Asymmetric peak fitting with two half-Gaussians"),
|
|
1311
|
+
)
|
|
1312
|
+
cra_fit(
|
|
1313
|
+
_("Piecewise exponential (raise-decay) fit"),
|
|
1314
|
+
fitdialog.piecewiseexponential_fit,
|
|
1315
|
+
tip=_(
|
|
1316
|
+
"Piecewise exponential fitting with raise and decay "
|
|
1317
|
+
"components"
|
|
1318
|
+
),
|
|
1319
|
+
)
|
|
1320
|
+
cra_fit(_("Exponential fit"), fitdialog.exponential_fit)
|
|
1321
|
+
cra_fit(_("Sinusoidal fit"), fitdialog.sinusoidal_fit)
|
|
1322
|
+
cra_fit(
|
|
1323
|
+
_("CDF fit"),
|
|
1324
|
+
fitdialog.cdf_fit,
|
|
1325
|
+
tip=_(
|
|
1326
|
+
"Cumulative distribution function fit, "
|
|
1327
|
+
"related to Error function (erf)"
|
|
1328
|
+
),
|
|
1329
|
+
)
|
|
1330
|
+
separator_needed = True
|
|
1331
|
+
for fit_name in (
|
|
1332
|
+
"linear_fit",
|
|
1333
|
+
"polynomial_fit",
|
|
1334
|
+
"gaussian_fit",
|
|
1335
|
+
"lorentzian_fit",
|
|
1336
|
+
"voigt_fit",
|
|
1337
|
+
"planckian_fit",
|
|
1338
|
+
"twohalfgaussian_fit",
|
|
1339
|
+
"piecewiseexponential_fit",
|
|
1340
|
+
"exponential_fit",
|
|
1341
|
+
"sinusoidal_fit",
|
|
1342
|
+
"cdf_fit",
|
|
1343
|
+
"sigmoid_fit",
|
|
1344
|
+
):
|
|
1345
|
+
self.action_for(fit_name, separator=separator_needed)
|
|
1346
|
+
separator_needed = False
|
|
1347
|
+
self.action_for("evaluate_fit", separator=True)
|
|
1348
|
+
self.action_for("derivative", separator=True)
|
|
1349
|
+
self.action_for("integral")
|
|
1350
|
+
self.action_for("apply_window", separator=True)
|
|
1351
|
+
self.action_for("detrending")
|
|
1352
|
+
self.action_for("interpolate")
|
|
1353
|
+
self.action_for("resampling")
|
|
1354
|
+
with self.new_menu(_("Stability analysis"), icon_name="stability.svg"):
|
|
1355
|
+
self.action_for("allan_variance")
|
|
1356
|
+
self.action_for("allan_deviation")
|
|
1357
|
+
self.action_for("modified_allan_variance")
|
|
1358
|
+
self.action_for("hadamard_variance")
|
|
1359
|
+
self.action_for("total_variance")
|
|
1360
|
+
self.action_for("time_deviation")
|
|
1361
|
+
self.new_action(
|
|
1362
|
+
_("All stability features") + "...",
|
|
1363
|
+
triggered=self.panel.processor.compute_all_stability,
|
|
1364
|
+
separator=True,
|
|
1365
|
+
tip=_("Compute all stability features"),
|
|
1366
|
+
)
|
|
1367
|
+
self.action_for("xy_mode", separator=True)
|
|
1368
|
+
|
|
1369
|
+
# MARK: ANALYSIS
|
|
1370
|
+
with self.new_category(ActionCategory.ANALYSIS):
|
|
1371
|
+
self.action_for("fwhm")
|
|
1372
|
+
self.action_for("fw1e2")
|
|
1373
|
+
self.new_action(
|
|
1374
|
+
_("Full width at y=..."),
|
|
1375
|
+
triggered=self.panel.processor.compute_full_width_at_y,
|
|
1376
|
+
tip=_("Compute the full width at a given y value"),
|
|
1377
|
+
)
|
|
1378
|
+
self.action_for("x_at_minmax")
|
|
1379
|
+
self.new_action(
|
|
1380
|
+
_("First abscissa at y=..."),
|
|
1381
|
+
triggered=self.panel.processor.compute_x_at_y,
|
|
1382
|
+
tip=_(
|
|
1383
|
+
"Compute the first abscissa at a given y value "
|
|
1384
|
+
"(linear interpolation)"
|
|
1385
|
+
),
|
|
1386
|
+
)
|
|
1387
|
+
self.new_action(
|
|
1388
|
+
_("Ordinate at x=..."),
|
|
1389
|
+
triggered=self.panel.processor.compute_y_at_x,
|
|
1390
|
+
tip=_("Compute the ordinate at a given x value (linear interpolation)"),
|
|
1391
|
+
)
|
|
1392
|
+
self.action_for("extract_pulse_features")
|
|
1393
|
+
self.new_action(
|
|
1394
|
+
_("Peak detection"),
|
|
1395
|
+
separator=True,
|
|
1396
|
+
triggered=self.panel.processor.compute_peak_detection,
|
|
1397
|
+
icon_name="peak_detect.svg",
|
|
1398
|
+
)
|
|
1399
|
+
self.action_for("sampling_rate_period", separator=True)
|
|
1400
|
+
self.action_for("dynamic_parameters", context_menu_pos=-1)
|
|
1401
|
+
self.action_for("bandwidth_3db", context_menu_pos=-1)
|
|
1402
|
+
self.action_for("contrast")
|
|
1403
|
+
|
|
1404
|
+
def create_last_actions(self):
|
|
1405
|
+
"""Create actions that are added to the menus in the end"""
|
|
1406
|
+
super().create_last_actions()
|
|
1407
|
+
with self.new_category(ActionCategory.OPERATION):
|
|
1408
|
+
self.action_for("signals_to_image", separator=True)
|
|
1409
|
+
|
|
1410
|
+
with self.new_category(ActionCategory.VIEW):
|
|
1411
|
+
antialiasing_action = self.new_action(
|
|
1412
|
+
_("Curve anti-aliasing"),
|
|
1413
|
+
icon_name="curve_antialiasing.svg",
|
|
1414
|
+
toggled=self.panel.toggle_anti_aliasing,
|
|
1415
|
+
tip=_("Toggle curve anti-aliasing on/off (may slow down plotting)"),
|
|
1416
|
+
toolbar_pos=-1,
|
|
1417
|
+
)
|
|
1418
|
+
antialiasing_action.setChecked(Conf.view.sig_antialiasing.get(True))
|
|
1419
|
+
self.new_action(
|
|
1420
|
+
_("Reset curve styles"),
|
|
1421
|
+
select_condition=SelectCond.always,
|
|
1422
|
+
icon_name="reset_curve_styles.svg",
|
|
1423
|
+
triggered=self.panel.reset_curve_styles,
|
|
1424
|
+
tip=_(
|
|
1425
|
+
"Curve styles are looped over a list of predefined styles.\n"
|
|
1426
|
+
"This action resets the list to its initial state."
|
|
1427
|
+
),
|
|
1428
|
+
toolbar_pos=-1,
|
|
1429
|
+
)
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
class ImageActionHandler(BaseActionHandler):
|
|
1433
|
+
"""Object handling image panel GUI interactions: actions, menus, ..."""
|
|
1434
|
+
|
|
1435
|
+
OBJECT_STR = _("image")
|
|
1436
|
+
OBJECT_STR_PLURAL = _("images")
|
|
1437
|
+
|
|
1438
|
+
def create_first_actions(self):
|
|
1439
|
+
"""Create actions that are added to the menus in the first place"""
|
|
1440
|
+
# MARK: PROCESSING (1/2)
|
|
1441
|
+
with self.new_category(ActionCategory.PROCESSING):
|
|
1442
|
+
with self.new_menu(_("Geometry"), icon_name="rotate_right.svg"):
|
|
1443
|
+
self.action_for("fliph", context_menu_pos=-1, context_menu_sep=True)
|
|
1444
|
+
self.action_for("transpose", context_menu_pos=-1)
|
|
1445
|
+
self.action_for("flipv", context_menu_pos=-1)
|
|
1446
|
+
self.action_for("rotate270", context_menu_pos=-1)
|
|
1447
|
+
self.action_for("rotate90", context_menu_pos=-1)
|
|
1448
|
+
self.action_for("rotate")
|
|
1449
|
+
self.new_action(
|
|
1450
|
+
_("Distribute on a grid..."),
|
|
1451
|
+
triggered=self.panel.processor.distribute_on_grid,
|
|
1452
|
+
icon_name="distribute_on_grid.svg",
|
|
1453
|
+
select_condition=SelectCond.at_least_two,
|
|
1454
|
+
separator=True,
|
|
1455
|
+
)
|
|
1456
|
+
self.new_action(
|
|
1457
|
+
_("Reset image positions"),
|
|
1458
|
+
triggered=self.panel.processor.reset_positions,
|
|
1459
|
+
icon_name="reset_positions.svg",
|
|
1460
|
+
select_condition=SelectCond.at_least_two,
|
|
1461
|
+
)
|
|
1462
|
+
|
|
1463
|
+
with self.new_menu(
|
|
1464
|
+
_("Axis transformation"), icon_name="axis_transform.svg"
|
|
1465
|
+
):
|
|
1466
|
+
self.action_for("set_uniform_coords")
|
|
1467
|
+
|
|
1468
|
+
super().create_first_actions()
|
|
1469
|
+
|
|
1470
|
+
# MARK: CREATE
|
|
1471
|
+
with self.new_category(ActionCategory.CREATE):
|
|
1472
|
+
for label, pclass, icon_name, separator in (
|
|
1473
|
+
(_("Zero"), sio.Zero2DParam, "2d-zero.svg", False),
|
|
1474
|
+
(
|
|
1475
|
+
_("Normal distribution"),
|
|
1476
|
+
sio.NormalDistribution2DParam,
|
|
1477
|
+
"2d-normal.svg",
|
|
1478
|
+
False,
|
|
1479
|
+
),
|
|
1480
|
+
(
|
|
1481
|
+
_("Poisson distribution"),
|
|
1482
|
+
sio.PoissonDistribution2DParam,
|
|
1483
|
+
"2d-poisson.svg",
|
|
1484
|
+
False,
|
|
1485
|
+
),
|
|
1486
|
+
(
|
|
1487
|
+
_("Uniform distribution"),
|
|
1488
|
+
sio.UniformDistribution2DParam,
|
|
1489
|
+
"2d-uniform.svg",
|
|
1490
|
+
False,
|
|
1491
|
+
),
|
|
1492
|
+
(_("Gaussian"), sio.Gauss2DParam, "2d-gaussian.svg", True),
|
|
1493
|
+
(_("2D sinc"), sio.Sinc2DParam, "2d-sinc.svg", False),
|
|
1494
|
+
(_("Ring pattern"), sio.Ring2DParam, "ring.svg", True),
|
|
1495
|
+
(_("Ramp"), sio.Ramp2DParam, "2d-ramp.svg", False),
|
|
1496
|
+
(_("Checkerboard"), sio.Checkerboard2DParam, "checkerboard.svg", False),
|
|
1497
|
+
(
|
|
1498
|
+
_("Sinusoidal grating"),
|
|
1499
|
+
sio.SinusoidalGrating2DParam,
|
|
1500
|
+
"grating.svg",
|
|
1501
|
+
False,
|
|
1502
|
+
),
|
|
1503
|
+
(_("Siemens star"), sio.SiemensStar2DParam, "siemens.svg", False),
|
|
1504
|
+
):
|
|
1505
|
+
self.new_action(
|
|
1506
|
+
label,
|
|
1507
|
+
tip=_("Create new %s") % label,
|
|
1508
|
+
triggered=lambda pclass=pclass: self.panel.new_object(pclass()),
|
|
1509
|
+
icon_name=icon_name,
|
|
1510
|
+
select_condition=SelectCond.always,
|
|
1511
|
+
separator=separator,
|
|
1512
|
+
)
|
|
1513
|
+
|
|
1514
|
+
# MARK: ROI
|
|
1515
|
+
with self.new_category(ActionCategory.ROI):
|
|
1516
|
+
self.new_action(
|
|
1517
|
+
_("Create ROI grid") + "...",
|
|
1518
|
+
triggered=self.panel.processor.create_roi_grid,
|
|
1519
|
+
icon_name="roi_grid.svg",
|
|
1520
|
+
tip=_("Create a grid of regions of interest"),
|
|
1521
|
+
)
|
|
1522
|
+
|
|
1523
|
+
# MARK: OPERATION
|
|
1524
|
+
with self.new_category(ActionCategory.OPERATION):
|
|
1525
|
+
self.action_for("log10_z_plus_n")
|
|
1526
|
+
|
|
1527
|
+
# MARK: PROCESSING (2/2)
|
|
1528
|
+
with self.new_category(ActionCategory.PROCESSING):
|
|
1529
|
+
with self.new_menu(_("Frequency filters"), icon_name="noise_reduction.svg"):
|
|
1530
|
+
self.action_for("butterworth")
|
|
1531
|
+
self.action_for("gaussian_freq_filter")
|
|
1532
|
+
with self.new_menu(_("Thresholding"), icon_name="thresholding.svg"):
|
|
1533
|
+
self.action_for("threshold")
|
|
1534
|
+
self.action_for("threshold_isodata")
|
|
1535
|
+
self.action_for("threshold_li")
|
|
1536
|
+
self.action_for("threshold_mean")
|
|
1537
|
+
self.action_for("threshold_minimum")
|
|
1538
|
+
self.action_for("threshold_otsu")
|
|
1539
|
+
self.action_for("threshold_triangle")
|
|
1540
|
+
self.action_for("threshold_yen")
|
|
1541
|
+
self.new_action(
|
|
1542
|
+
_("All thresholding methods") + "...",
|
|
1543
|
+
triggered=self.panel.processor.compute_all_threshold,
|
|
1544
|
+
separator=True,
|
|
1545
|
+
tip=_("Apply all thresholding methods"),
|
|
1546
|
+
)
|
|
1547
|
+
with self.new_menu(_("Exposure"), icon_name="exposure.svg"):
|
|
1548
|
+
self.action_for("adjust_gamma")
|
|
1549
|
+
self.action_for("adjust_log")
|
|
1550
|
+
self.action_for("adjust_sigmoid")
|
|
1551
|
+
self.action_for("equalize_hist")
|
|
1552
|
+
self.action_for("equalize_adapthist")
|
|
1553
|
+
self.action_for("rescale_intensity")
|
|
1554
|
+
with self.new_menu(_("Restoration"), icon_name="noise_reduction.svg"):
|
|
1555
|
+
self.action_for("denoise_tv")
|
|
1556
|
+
self.action_for("denoise_bilateral")
|
|
1557
|
+
self.action_for("denoise_wavelet")
|
|
1558
|
+
self.action_for("denoise_tophat")
|
|
1559
|
+
self.new_action(
|
|
1560
|
+
_("All denoising methods") + "...",
|
|
1561
|
+
triggered=self.panel.processor.compute_all_denoise,
|
|
1562
|
+
separator=True,
|
|
1563
|
+
tip=_("Apply all denoising methods"),
|
|
1564
|
+
)
|
|
1565
|
+
with self.new_menu(_("Morphology"), icon_name="morphology.svg"):
|
|
1566
|
+
self.action_for("white_tophat")
|
|
1567
|
+
self.action_for("black_tophat")
|
|
1568
|
+
self.action_for("erosion")
|
|
1569
|
+
self.action_for("dilation")
|
|
1570
|
+
self.action_for("opening")
|
|
1571
|
+
self.action_for("closing")
|
|
1572
|
+
self.new_action(
|
|
1573
|
+
_("All morphological operations") + "...",
|
|
1574
|
+
triggered=self.panel.processor.compute_all_morphology,
|
|
1575
|
+
separator=True,
|
|
1576
|
+
tip=_("Apply all morphological operations"),
|
|
1577
|
+
)
|
|
1578
|
+
with self.new_menu(_("Edge detection"), icon_name="edge_detection.svg"):
|
|
1579
|
+
self.action_for("canny")
|
|
1580
|
+
self.action_for("farid", separator=True)
|
|
1581
|
+
self.action_for("farid_h")
|
|
1582
|
+
self.action_for("farid_v")
|
|
1583
|
+
self.action_for("laplace", separator=True)
|
|
1584
|
+
self.action_for("prewitt", separator=True)
|
|
1585
|
+
self.action_for("prewitt_h")
|
|
1586
|
+
self.action_for("prewitt_v")
|
|
1587
|
+
self.action_for("roberts", separator=True)
|
|
1588
|
+
self.action_for("scharr", separator=True)
|
|
1589
|
+
self.action_for("scharr_h")
|
|
1590
|
+
self.action_for("scharr_v")
|
|
1591
|
+
self.action_for("sobel", separator=True)
|
|
1592
|
+
self.action_for("sobel_h")
|
|
1593
|
+
self.action_for("sobel_v")
|
|
1594
|
+
self.new_action(
|
|
1595
|
+
_("All edge detection filters..."),
|
|
1596
|
+
triggered=self.panel.processor.compute_all_edges,
|
|
1597
|
+
separator=True,
|
|
1598
|
+
tip=_("Compute all edge detection filters"),
|
|
1599
|
+
)
|
|
1600
|
+
self.new_action(
|
|
1601
|
+
_("Erase area") + "...",
|
|
1602
|
+
triggered=self.panel.processor.compute_erase,
|
|
1603
|
+
icon_name="erase.svg",
|
|
1604
|
+
separator=True,
|
|
1605
|
+
tip=_("Erase area in the image as defined by a region of interest"),
|
|
1606
|
+
)
|
|
1607
|
+
|
|
1608
|
+
# MARK: ANALYSIS
|
|
1609
|
+
with self.new_category(ActionCategory.ANALYSIS):
|
|
1610
|
+
with self.new_menu(_("Intensity profiles"), icon_name="profile.svg"):
|
|
1611
|
+
self.new_action(
|
|
1612
|
+
_("Line profile..."),
|
|
1613
|
+
triggered=self.panel.processor.compute_line_profile,
|
|
1614
|
+
icon_name="profile.svg",
|
|
1615
|
+
tip=_("Extract horizontal or vertical profile"),
|
|
1616
|
+
context_menu_pos=-1,
|
|
1617
|
+
context_menu_sep=True,
|
|
1618
|
+
)
|
|
1619
|
+
self.new_action(
|
|
1620
|
+
_("Segment profile..."),
|
|
1621
|
+
triggered=self.panel.processor.compute_segment_profile,
|
|
1622
|
+
icon_name="profile_segment.svg",
|
|
1623
|
+
tip=_("Extract profile along a segment"),
|
|
1624
|
+
context_menu_pos=-1,
|
|
1625
|
+
)
|
|
1626
|
+
self.new_action(
|
|
1627
|
+
_("Average profile..."),
|
|
1628
|
+
triggered=self.panel.processor.compute_average_profile,
|
|
1629
|
+
icon_name="profile_average.svg",
|
|
1630
|
+
tip=_("Extract average horizontal or vertical profile"),
|
|
1631
|
+
context_menu_pos=-1,
|
|
1632
|
+
)
|
|
1633
|
+
self.new_action(
|
|
1634
|
+
_("Radial profile extraction..."),
|
|
1635
|
+
triggered=self.panel.processor.compute_radial_profile,
|
|
1636
|
+
icon_name="profile_radial.svg",
|
|
1637
|
+
tip=_("Radial profile extraction around image centroid"),
|
|
1638
|
+
)
|
|
1639
|
+
self.action_for("horizontal_projection", separator=True)
|
|
1640
|
+
self.action_for("vertical_projection")
|
|
1641
|
+
self.action_for("centroid", separator=True)
|
|
1642
|
+
self.action_for("enclosing_circle")
|
|
1643
|
+
self.new_action(
|
|
1644
|
+
_("2D peak detection"),
|
|
1645
|
+
separator=True,
|
|
1646
|
+
triggered=self.panel.processor.compute_peak_detection,
|
|
1647
|
+
tip=_("Compute automatic 2D peak detection"),
|
|
1648
|
+
)
|
|
1649
|
+
self.action_for("contour_shape")
|
|
1650
|
+
self.action_for("hough_circle_peaks")
|
|
1651
|
+
with self.new_menu(_("Blob detection")):
|
|
1652
|
+
self.action_for("blob_dog")
|
|
1653
|
+
self.action_for("blob_doh")
|
|
1654
|
+
self.action_for("blob_log")
|
|
1655
|
+
self.action_for("blob_opencv")
|
|
1656
|
+
|
|
1657
|
+
def create_last_actions(self):
|
|
1658
|
+
"""Create actions that are added to the menus in the end"""
|
|
1659
|
+
# MARK: PROCESSING
|
|
1660
|
+
with self.new_category(ActionCategory.PROCESSING):
|
|
1661
|
+
self.new_action(
|
|
1662
|
+
_("Resize") + "...",
|
|
1663
|
+
triggered=self.panel.processor.compute_resize,
|
|
1664
|
+
icon_name="resize.svg",
|
|
1665
|
+
separator=True,
|
|
1666
|
+
)
|
|
1667
|
+
self.new_action(
|
|
1668
|
+
_("Pixel binning") + "...",
|
|
1669
|
+
triggered=self.panel.processor.compute_binning,
|
|
1670
|
+
icon_name="binning.svg",
|
|
1671
|
+
)
|
|
1672
|
+
self.action_for("resampling")
|
|
1673
|
+
|
|
1674
|
+
# MARK: VIEW
|
|
1675
|
+
with self.new_category(ActionCategory.VIEW):
|
|
1676
|
+
self.new_action(
|
|
1677
|
+
_("View images side-by-side") + "...",
|
|
1678
|
+
icon_name="new_window.svg",
|
|
1679
|
+
tip=_("View selected images side-by-side in a new window"),
|
|
1680
|
+
triggered=self.panel.view_images_side_by_side,
|
|
1681
|
+
select_condition=SelectCond.at_least_two,
|
|
1682
|
+
context_menu_pos=-1,
|
|
1683
|
+
)
|
|
1684
|
+
|
|
1685
|
+
super().create_last_actions()
|
|
1686
|
+
|
|
1687
|
+
# MARK: OPERATION
|
|
1688
|
+
with self.new_category(ActionCategory.OPERATION):
|
|
1689
|
+
self.action_for("flatfield", separator=True)
|
|
1690
|
+
|
|
1691
|
+
# MARK: VIEW
|
|
1692
|
+
with self.new_category(ActionCategory.VIEW):
|
|
1693
|
+
showcontrast_action = self.new_action(
|
|
1694
|
+
_("Show contrast panel"),
|
|
1695
|
+
icon_name="contrast.png",
|
|
1696
|
+
tip=_("Show or hide contrast adjustment panel"),
|
|
1697
|
+
select_condition=SelectCond.always,
|
|
1698
|
+
toggled=self.panel.toggle_show_contrast,
|
|
1699
|
+
toolbar_pos=-1,
|
|
1700
|
+
)
|
|
1701
|
+
showcontrast_action.setChecked(Conf.view.show_contrast.get(True))
|