tomwer 1.2.1__py3-none-any.whl → 1.3.12__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.
- orangecontrib/tomwer/tutorials/icat_publication.ows +58 -0
- orangecontrib/tomwer/widgets/__init__.py +11 -11
- orangecontrib/tomwer/widgets/control/DataDiscoveryOW.py +2 -2
- orangecontrib/tomwer/widgets/control/DataListOW.py +9 -7
- orangecontrib/tomwer/widgets/control/DataListenerOW.py +6 -6
- orangecontrib/tomwer/widgets/control/DataSelectorOW.py +21 -10
- orangecontrib/tomwer/widgets/control/DataValidatorOW.py +6 -6
- orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +24 -7
- orangecontrib/tomwer/widgets/control/EmailOW.py +4 -4
- orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +3 -3
- orangecontrib/tomwer/widgets/control/NXTomomillOW.py +64 -23
- orangecontrib/tomwer/widgets/control/NXtomoConcatenate.py +20 -8
- orangecontrib/tomwer/widgets/control/NotifierOW.py +1 -0
- orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +6 -6
- orangecontrib/tomwer/widgets/control/VolumeSelector.py +7 -4
- orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +182 -182
- orangecontrib/tomwer/widgets/debugtools/DatasetGeneratorOW.py +5 -5
- orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +4 -4
- orangecontrib/tomwer/widgets/edit/ImageKeyEditorOW.py +3 -3
- orangecontrib/tomwer/widgets/edit/ImageKeyUpgraderOW.py +8 -1
- orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +3 -3
- orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +3 -3
- orangecontrib/tomwer/widgets/icat/PublishProcessedDataOW.py +115 -0
- orangecontrib/tomwer/widgets/icat/RawDataScreenshotCreatorOW.py +98 -0
- orangecontrib/tomwer/widgets/icat/SaveToGalleryAndPublishOW.py +129 -0
- orangecontrib/tomwer/widgets/icat/__init__.py +13 -0
- orangecontrib/tomwer/widgets/icat/icons/add_gallery.png +0 -0
- orangecontrib/tomwer/widgets/icat/icons/add_gallery.svg +82 -0
- orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.png +0 -0
- orangecontrib/tomwer/widgets/icat/icons/publish_processed_data.svg +95 -0
- orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.png +0 -0
- orangecontrib/tomwer/widgets/icat/icons/raw_screenshots.svg +143 -0
- orangecontrib/tomwer/widgets/icons/tomwer_data_portal.png +0 -0
- orangecontrib/tomwer/widgets/icons/tomwer_data_portal.svg +76 -0
- orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +22 -20
- orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +19 -3
- orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +184 -169
- orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +23 -0
- orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +39 -5
- orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +18 -22
- orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +18 -26
- orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +15 -19
- orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +9 -9
- orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
- orangecontrib/tomwer/widgets/visualization/LivesliceOW.py +1 -1
- orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +3 -3
- orangecontrib/tomwer/widgets/visualization/SinogramViewerOW.py +0 -1
- orangecontrib/tomwer/widgets/visualization/VolumeViewerOW.py +3 -29
- tomwer/__main__.py +7 -64
- tomwer/app/axis.py +3 -3
- tomwer/app/canvas.py +8 -0
- tomwer/app/canvas_launcher/config.py +16 -14
- tomwer/app/canvas_launcher/environ.py +1 -0
- tomwer/app/canvas_launcher/mainwindow.py +4 -1
- tomwer/app/darkref.py +1 -1
- tomwer/app/darkrefpatch.py +1 -1
- tomwer/app/diffframe.py +3 -3
- tomwer/app/imagekeyeditor.py +5 -5
- tomwer/app/imagekeyupgrader.py +5 -5
- tomwer/app/intensitynormalization.py +14 -13
- tomwer/app/{saaxis.py → multicor.py} +3 -3
- tomwer/app/{sadeltabeta.py → multipag.py} +3 -3
- tomwer/app/nabuapp.py +0 -11
- tomwer/app/radiostack.py +6 -4
- tomwer/app/samplemoved.py +3 -2
- tomwer/app/scanviewer.py +4 -2
- tomwer/app/sinogramviewer.py +3 -2
- tomwer/app/slicestack.py +3 -2
- tomwer/app/zstitching.py +88 -6
- tomwer/core/cluster/cluster.py +26 -0
- tomwer/core/log/logger.py +7 -5
- tomwer/core/process/conditions/filters.py +1 -1
- tomwer/core/process/control/datalistener/datalistener.py +19 -14
- tomwer/core/process/control/datawatcher/edfdwprocess.py +0 -9
- tomwer/core/process/control/nxtomoconcatenate.py +13 -13
- tomwer/core/process/control/nxtomomill.py +92 -34
- tomwer/core/process/control/scantransfer.py +20 -43
- tomwer/core/process/control/scanvalidator.py +3 -2
- tomwer/core/process/control/test/test_concatenate_nxtomos.py +9 -9
- tomwer/core/process/control/test/test_email.py +4 -4
- tomwer/core/process/control/test/test_h52nx_process.py +59 -7
- tomwer/core/process/control/test/test_volume_link.py +64 -64
- tomwer/core/process/control/timer.py +1 -1
- tomwer/core/process/control/volumesymlink.py +200 -200
- tomwer/core/process/edit/darkflatpatch.py +14 -15
- tomwer/core/process/edit/imagekeyeditor.py +41 -39
- tomwer/core/process/icat/__init__.py +0 -0
- tomwer/core/process/icat/createscreenshots.py +100 -0
- tomwer/core/process/icat/gallery.py +377 -0
- tomwer/core/process/icat/icatbase.py +36 -0
- tomwer/core/process/icat/publish.py +228 -0
- tomwer/core/process/icat/screenshots.py +27 -0
- tomwer/core/process/output.py +52 -0
- tomwer/core/process/reconstruction/axis/axis.py +280 -69
- tomwer/core/process/reconstruction/axis/mode.py +163 -48
- tomwer/core/process/reconstruction/axis/params.py +29 -21
- tomwer/core/process/reconstruction/darkref/darkrefs.py +41 -127
- tomwer/core/process/reconstruction/darkref/darkrefscopy.py +4 -3
- tomwer/core/process/reconstruction/darkref/params.py +1 -1
- tomwer/core/process/reconstruction/nabu/castvolume.py +4 -4
- tomwer/core/process/reconstruction/nabu/helical.py +9 -5
- tomwer/core/process/reconstruction/nabu/nabucommon.py +71 -78
- tomwer/core/process/reconstruction/nabu/nabuscores.py +425 -53
- tomwer/core/process/reconstruction/nabu/nabuslices.py +114 -93
- tomwer/core/process/reconstruction/nabu/nabuvolume.py +54 -27
- tomwer/core/process/reconstruction/nabu/plane.py +9 -0
- tomwer/core/process/reconstruction/nabu/settings.py +2 -2
- tomwer/core/process/reconstruction/nabu/utils.py +164 -26
- tomwer/core/process/reconstruction/output.py +108 -0
- tomwer/core/process/reconstruction/saaxis/saaxis.py +238 -264
- tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +151 -87
- tomwer/core/process/reconstruction/scores/params.py +7 -4
- tomwer/core/process/reconstruction/scores/scores.py +13 -0
- tomwer/core/process/reconstruction/test/test_axis_params.py +2 -2
- tomwer/core/process/reconstruction/test/test_darkref.py +3 -3
- tomwer/core/process/reconstruction/test/test_darkref_copy.py +7 -7
- tomwer/core/process/reconstruction/test/test_saaxis.py +3 -4
- tomwer/core/process/reconstruction/test/test_sadeltabeta.py +2 -2
- tomwer/core/process/stitching/nabustitcher.py +13 -12
- tomwer/core/process/task.py +34 -26
- tomwer/core/process/test/test_axis.py +13 -12
- tomwer/core/process/test/test_dark_and_flat.py +10 -7
- tomwer/core/process/test/test_data_transfer.py +10 -8
- tomwer/core/process/test/test_nabu.py +14 -6
- tomwer/core/process/test/test_normalization.py +4 -4
- tomwer/core/scan/blissscan.py +3 -3
- tomwer/core/scan/edfscan.py +13 -10
- tomwer/core/scan/hdf5scan.py +19 -530
- tomwer/core/scan/nxtomoscan.py +534 -0
- tomwer/core/scan/scanbase.py +72 -44
- tomwer/core/scan/scanfactory.py +13 -13
- tomwer/core/scan/test/test_edf.py +2 -2
- tomwer/core/scan/test/test_future_scan.py +3 -3
- tomwer/core/scan/test/test_h5.py +18 -16
- tomwer/core/scan/test/test_process_registration.py +4 -40
- tomwer/core/scan/test/test_scan.py +5 -78
- tomwer/core/settings.py +22 -2
- tomwer/core/test/test_scanutils.py +8 -7
- tomwer/core/test/test_utils.py +35 -28
- tomwer/core/tomwer_object.py +1 -1
- tomwer/core/utils/__init__.py +0 -466
- tomwer/core/utils/deprecation.py +1 -1
- tomwer/core/utils/dictutils.py +14 -0
- tomwer/core/utils/lbsram.py +35 -0
- tomwer/core/utils/nxtomoutils.py +1 -1
- tomwer/core/utils/scanutils.py +6 -6
- tomwer/core/utils/spec.py +263 -0
- tomwer/core/volume/edfvolume.py +6 -6
- tomwer/core/volume/hdf5volume.py +6 -6
- tomwer/core/volume/jp2kvolume.py +6 -6
- tomwer/core/volume/rawvolume.py +6 -6
- tomwer/core/volume/tiffvolume.py +12 -12
- tomwer/core/volume/volumefactory.py +2 -2
- tomwer/gui/cluster/slurm.py +274 -65
- tomwer/gui/cluster/supervisor.py +12 -0
- tomwer/gui/cluster/test/test_cluster.py +14 -2
- tomwer/gui/cluster/test/test_supervisor.py +3 -3
- tomwer/gui/configuration/__init__.py +0 -0
- tomwer/gui/{reconstruction/nabu → configuration}/action.py +1 -32
- tomwer/gui/configuration/level.py +22 -0
- tomwer/gui/control/actions.py +54 -0
- tomwer/gui/control/datalist.py +83 -16
- tomwer/gui/control/datalistener.py +4 -16
- tomwer/gui/control/datawatcher/controlwidget.py +2 -4
- tomwer/gui/control/datawatcher/datawatcher.py +1 -24
- tomwer/gui/control/{email.py → emailnotifier.py} +9 -18
- tomwer/gui/control/history.py +2 -2
- tomwer/gui/control/observations.py +2 -2
- tomwer/gui/control/reducedarkflatselector.py +9 -9
- tomwer/gui/control/selectorwidgetbase.py +36 -9
- tomwer/gui/control/serie/seriecreator.py +5 -22
- tomwer/gui/control/test/test_email.py +1 -1
- tomwer/gui/control/test/test_scanvalidator.py +6 -5
- tomwer/gui/control/test/test_single_tomo_obj.py +3 -3
- tomwer/gui/control/tomoobjdisplaymode.py +8 -0
- tomwer/gui/debugtools/datasetgenerator.py +3 -3
- tomwer/gui/edit/dkrfpatch.py +20 -26
- tomwer/gui/edit/imagekeyeditor.py +11 -12
- tomwer/gui/edit/nxtomoeditor.py +111 -44
- tomwer/gui/edit/nxtomowarmer.py +7 -6
- tomwer/gui/edit/test/test_dkrf_patch.py +13 -13
- tomwer/gui/edit/test/test_image_key_editor.py +3 -3
- tomwer/gui/edit/test/test_nx_editor.py +40 -16
- tomwer/gui/icat/__init__.py +0 -0
- tomwer/gui/icat/createscreenshots.py +80 -0
- tomwer/gui/icat/gallery.py +214 -0
- tomwer/gui/icat/publish.py +187 -0
- tomwer/gui/imagefromfile.py +2 -2
- tomwer/gui/qfolderdialog.py +24 -1
- tomwer/gui/reconstruction/axis/CompareImages.py +88 -168
- tomwer/gui/reconstruction/axis/axis.py +171 -57
- tomwer/gui/reconstruction/axis/radioaxis.py +122 -257
- tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +3 -2
- tomwer/gui/reconstruction/darkref/darkrefwidget.py +2 -1
- tomwer/gui/reconstruction/nabu/castvolume.py +14 -3
- tomwer/gui/reconstruction/nabu/check.py +9 -9
- tomwer/gui/reconstruction/nabu/helical.py +29 -12
- tomwer/gui/reconstruction/nabu/nabuconfig/base.py +2 -4
- tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +2 -1
- tomwer/gui/reconstruction/nabu/nabuconfig/output.py +126 -35
- tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +39 -32
- tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +222 -31
- tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +57 -27
- tomwer/gui/reconstruction/nabu/nabuflow.py +12 -20
- tomwer/gui/reconstruction/nabu/slices.py +10 -11
- tomwer/gui/reconstruction/nabu/volume.py +22 -10
- tomwer/gui/reconstruction/normalization/intensity.py +18 -48
- tomwer/gui/reconstruction/saaxis/corrangeselector.py +8 -24
- tomwer/gui/reconstruction/saaxis/dimensionwidget.py +1 -1
- tomwer/gui/reconstruction/saaxis/saaxis.py +9 -21
- tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +45 -12
- tomwer/gui/reconstruction/scores/control.py +2 -9
- tomwer/gui/reconstruction/scores/scoreplot.py +13 -11
- tomwer/gui/reconstruction/test/test_axis.py +41 -16
- tomwer/gui/reconstruction/test/test_nabu.py +31 -9
- tomwer/gui/reconstruction/test/test_saaxis.py +3 -3
- tomwer/gui/reconstruction/test/test_sadeltabeta.py +12 -2
- tomwer/gui/settings.py +5 -28
- tomwer/gui/stackplot.py +2 -5
- tomwer/gui/stitching/action.py +49 -0
- tomwer/gui/stitching/config/axisparams.py +7 -24
- tomwer/gui/stitching/config/output.py +10 -8
- tomwer/gui/stitching/config/positionoveraxis.py +22 -23
- tomwer/gui/stitching/normalization.py +117 -0
- tomwer/gui/stitching/stitchandbackground.py +4 -6
- tomwer/gui/stitching/stitching.py +267 -45
- tomwer/gui/stitching/stitching_preview.py +62 -55
- tomwer/gui/stitching/stitching_raw.py +13 -12
- tomwer/gui/stitching/z_stitching/fineestimation.py +0 -60
- tomwer/gui/utils/buttons.py +112 -29
- tomwer/gui/utils/inputwidget.py +43 -25
- tomwer/gui/utils/lineselector/lineselector.py +1 -1
- tomwer/gui/utils/scandescription.py +4 -0
- tomwer/gui/utils/step.py +144 -0
- tomwer/gui/utils/unitsystem.py +2 -5
- tomwer/gui/utils/vignettes.py +176 -15
- tomwer/gui/visualization/dataviewer.py +48 -35
- tomwer/gui/visualization/diffviewer/diffviewer.py +7 -16
- tomwer/gui/visualization/diffviewer/shiftwidget.py +2 -5
- tomwer/gui/visualization/scanoverview.py +1 -1
- tomwer/gui/visualization/sinogramviewer.py +20 -36
- tomwer/gui/visualization/test/test_diffviewer.py +3 -3
- tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +4 -4
- tomwer/gui/visualization/test/test_sinogramviewer.py +2 -2
- tomwer/gui/visualization/test/test_stacks.py +3 -3
- tomwer/gui/visualization/test/test_volumeviewer.py +65 -67
- tomwer/gui/visualization/volumeviewer.py +114 -113
- tomwer/io/utils/h5pyutils.py +3 -3
- tomwer/io/utils/raw_and_processed_data.py +84 -0
- tomwer/io/utils/tomoobj.py +4 -6
- tomwer/io/utils/utils.py +7 -7
- tomwer/resources/gui/icons/parameters.svg +1 -1
- tomwer/resources/gui/icons/ruler.png +0 -0
- tomwer/resources/gui/icons/ruler.svg +273 -0
- tomwer/resources/gui/icons/short_description.png +0 -0
- tomwer/resources/gui/icons/short_description.svg +58 -0
- tomwer/resources/gui/icons/url.png +0 -0
- tomwer/resources/gui/icons/url.svg +58 -0
- tomwer/resources/gui/illustrations/no_rot.svg +1 -1
- tomwer/synctools/stacks/edit/darkflatpatch.py +19 -14
- tomwer/synctools/stacks/edit/imagekeyeditor.py +2 -2
- tomwer/synctools/stacks/reconstruction/axis.py +4 -4
- tomwer/synctools/stacks/reconstruction/castvolume.py +22 -7
- tomwer/synctools/stacks/reconstruction/dkrefcopy.py +25 -20
- tomwer/synctools/stacks/reconstruction/nabu.py +2 -2
- tomwer/synctools/stacks/reconstruction/normalization.py +2 -2
- tomwer/synctools/stacks/reconstruction/saaxis.py +2 -2
- tomwer/synctools/stacks/reconstruction/sadeltabeta.py +2 -2
- tomwer/synctools/test/test_darkRefs.py +7 -58
- tomwer/synctools/test/test_foldertransfer.py +6 -6
- tomwer/synctools/utils/scanstages.py +6 -6
- tomwer/tests/conftest.py +34 -0
- tomwer/tests/datasets.py +13 -0
- tomwer/tests/test_scripts.py +91 -41
- tomwer/tests/utils.py +5 -0
- tomwer/third_part/WaitingOverlay.py +110 -0
- tomwer/third_part/__init__.py +0 -0
- tomwer/version.py +2 -2
- tomwer-1.3.12-py3.11-nspkg.pth +1 -0
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/METADATA +73 -58
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/RECORD +287 -286
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/WHEEL +1 -1
- orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +0 -197
- orangecontrib/tomwer/widgets/reconstruction/icons/XY_lamino.svg +0 -168
- orangecontrib/tomwer/widgets/reconstruction/icons/XZ_lamino.svg +0 -275
- orangecontrib/tomwer/widgets/reconstruction/icons/YZ_lamino.svg +0 -182
- tomwer/app/lamino.py +0 -143
- tomwer/core/process/reconstruction/lamino/__init__.py +0 -1
- tomwer/core/process/reconstruction/lamino/tofu.py +0 -1000
- tomwer/core/process/test/test_lamino.py +0 -76
- tomwer/core/test/test_lamino.py +0 -92
- tomwer/gui/reconstruction/lamino/__init__.py +0 -31
- tomwer/gui/reconstruction/lamino/tofu/TofuOptionLoader.py +0 -107
- tomwer/gui/reconstruction/lamino/tofu/__init__.py +0 -1
- tomwer/gui/reconstruction/lamino/tofu/misc.py +0 -148
- tomwer/gui/reconstruction/lamino/tofu/projections.py +0 -896
- tomwer/gui/reconstruction/lamino/tofu/settings.py +0 -75
- tomwer/gui/reconstruction/lamino/tofu/tofu.py +0 -432
- tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +0 -567
- tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +0 -760
- tomwer/gui/reconstruction/test/test_lamino.py +0 -189
- tomwer/resources/gui/icons/esrf_1.svg +0 -307
- tomwer/resources/gui/icons/lamino_parameters.svg +0 -70
- tomwer/resources/gui/icons/triangle.svg +0 -80
- tomwer/resources/gui/illustrations/lamino_angle.png +0 -0
- tomwer/resources/gui/illustrations/lamino_angle.svg +0 -509
- tomwer/resources/gui/illustrations/lamino_beta_angle.png +0 -0
- tomwer/resources/gui/illustrations/lamino_beta_angle.svg +0 -97
- tomwer/resources/gui/illustrations/lamino_theta_angle.png +0 -0
- tomwer/resources/gui/illustrations/lamino_theta_angle.svg +0 -368
- tomwer/resources/gui/illustrations/manual_slice.png +0 -0
- tomwer/resources/gui/illustrations/manual_slice.svg +0 -221
- tomwer/resources/gui/illustrations/psi_angle.png +0 -0
- tomwer/resources/gui/illustrations/psi_angle.svg +0 -479
- tomwer/resources/gui/illustrations/rotation_center.png +0 -0
- tomwer/resources/gui/illustrations/rotation_center.svg +0 -276
- tomwer/resources/gui/illustrations/slice_stack.png +0 -0
- tomwer/resources/gui/illustrations/slice_stack.svg +0 -266
- tomwer/resources/gui/illustrations/xy_slice.png +0 -0
- tomwer/resources/gui/illustrations/xy_slice.svg +0 -269
- tomwer/resources/gui/illustrations/xz_slice.png +0 -0
- tomwer/resources/gui/illustrations/xz_slice.svg +0 -270
- tomwer/resources/gui/illustrations/yz_slice.png +0 -0
- tomwer/resources/gui/illustrations/yz_slice.svg +0 -270
- tomwer/synctools/stacks/reconstruction/lamino.py +0 -233
- tomwer/synctools/test/test_scanstages.py +0 -162
- tomwer/tests/utils/__init__.py +0 -247
- tomwer/tests/utils/utilstest.py +0 -220
- tomwer-1.2.1-py3.11-nspkg.pth +0 -1
- /tomwer/core/process/control/{email.py → emailnotifier.py} +0 -0
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/LICENSE +0 -0
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/entry_points.txt +0 -0
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/namespace_packages.txt +0 -0
- {tomwer-1.2.1.dist-info → tomwer-1.3.12.dist-info}/top_level.txt +0 -0
tomwer/gui/utils/step.py
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
import functools
|
2
|
+
from silx.gui import qt
|
3
|
+
from typing import Optional, Union
|
4
|
+
from tomwer.gui.utils.qt_utils import block_signals
|
5
|
+
|
6
|
+
|
7
|
+
class StepSizeSelectorWidget(qt.QGroupBox):
|
8
|
+
"""
|
9
|
+
Widget to define some steps size (as float). Used by the Axis and the AxisOrdered widgets
|
10
|
+
|
11
|
+
:param str title: title to provide to the group box
|
12
|
+
:param str label: text for the label set at the left of the QLineEdit
|
13
|
+
:param fine_value: (optional) value to provide for 'fine' step
|
14
|
+
:param medium_value: (optional) value to provide for 'medium' step
|
15
|
+
:param rough_value: (optional) value to provide for 'rough' step
|
16
|
+
:param dtype: type of the step. Can be int or float
|
17
|
+
"""
|
18
|
+
|
19
|
+
valueChanged = qt.Signal()
|
20
|
+
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
parent=None,
|
24
|
+
title="",
|
25
|
+
label: str = "step size",
|
26
|
+
fine_value: [float, int, None] = 0.1,
|
27
|
+
medium_value: [float, int, None] = 0.5,
|
28
|
+
rough_value: [float, int, None] = 1.0,
|
29
|
+
unit: Optional[str] = "px",
|
30
|
+
dtype: Union[int, float] = float,
|
31
|
+
):
|
32
|
+
assert fine_value is None or isinstance(
|
33
|
+
fine_value, dtype
|
34
|
+
), f"fine_value is expected to be None or a {dtype}. Get {type(fine_value)}"
|
35
|
+
assert medium_value is None or isinstance(
|
36
|
+
medium_value, dtype
|
37
|
+
), f"medium_value is expected to be None or a {dtype}. Get {type(medium_value)}"
|
38
|
+
assert rough_value is None or isinstance(
|
39
|
+
rough_value, dtype
|
40
|
+
), f"rough_value is expected to be None or a {dtype}. Get {type(rough_value)}"
|
41
|
+
super().__init__(title, parent)
|
42
|
+
self._dtype = dtype
|
43
|
+
if unit is None:
|
44
|
+
unit = ""
|
45
|
+
else:
|
46
|
+
unit = f" {unit}"
|
47
|
+
self.setLayout(qt.QGridLayout())
|
48
|
+
self.layout().setSpacing(2)
|
49
|
+
|
50
|
+
# label
|
51
|
+
self.layout().addWidget(qt.QLabel(label), 0, 0, 3, 1)
|
52
|
+
|
53
|
+
# QLE manual step size
|
54
|
+
default_value = medium_value or fine_value or rough_value
|
55
|
+
self._manualLE = qt.QLineEdit(str(default_value), parent=self)
|
56
|
+
if dtype is float:
|
57
|
+
validator = qt.QDoubleValidator(parent=self._manualLE, decimals=2)
|
58
|
+
validator.setBottom(0.0)
|
59
|
+
elif dtype is int:
|
60
|
+
validator = qt.QIntValidator(parent=self._manualLE)
|
61
|
+
validator.setBottom(0)
|
62
|
+
else:
|
63
|
+
raise ValueError("dtype is expected to be int or float")
|
64
|
+
self._manualLE.setValidator(validator)
|
65
|
+
self.layout().addWidget(self._manualLE, 0, 1, 3, 1)
|
66
|
+
|
67
|
+
# buttons
|
68
|
+
buttons_font = self.font()
|
69
|
+
buttons_font.setPixelSize(10)
|
70
|
+
self._expectedValues = {}
|
71
|
+
# for each button associate the expected value
|
72
|
+
|
73
|
+
# fine
|
74
|
+
if fine_value is not None:
|
75
|
+
self._fineButton = qt.QPushButton(f"fine ({fine_value}{unit})", parent=self)
|
76
|
+
self._fineButton.setCheckable(True)
|
77
|
+
self._fineButton.setFont(buttons_font)
|
78
|
+
self.layout().addWidget(self._fineButton, 0, 2, 1, 1)
|
79
|
+
self._fineButton.released.connect(
|
80
|
+
functools.partial(self.setStepSize, fine_value)
|
81
|
+
)
|
82
|
+
self._expectedValues[self._fineButton] = fine_value
|
83
|
+
else:
|
84
|
+
self._fineButton = None
|
85
|
+
|
86
|
+
# medium
|
87
|
+
if medium_value is not None:
|
88
|
+
self._mediumButton = qt.QPushButton(
|
89
|
+
f"medium ({medium_value}{unit})", parent=self
|
90
|
+
)
|
91
|
+
self._mediumButton.setCheckable(True)
|
92
|
+
self._mediumButton.setFont(buttons_font)
|
93
|
+
self.layout().addWidget(self._mediumButton, 1, 2, 1, 1)
|
94
|
+
self._mediumButton.released.connect(
|
95
|
+
functools.partial(self.setStepSize, medium_value)
|
96
|
+
)
|
97
|
+
self._expectedValues[self._mediumButton] = medium_value
|
98
|
+
else:
|
99
|
+
self._mediumButton = None
|
100
|
+
|
101
|
+
# rough
|
102
|
+
if rough_value is not None:
|
103
|
+
self._roughButton = qt.QPushButton(
|
104
|
+
f"rough ({rough_value}{unit})", parent=self
|
105
|
+
)
|
106
|
+
self._roughButton.setCheckable(True)
|
107
|
+
self._roughButton.setFont(buttons_font)
|
108
|
+
self.layout().addWidget(self._roughButton, 2, 2, 1, 1)
|
109
|
+
self._roughButton.released.connect(
|
110
|
+
functools.partial(self.setStepSize, rough_value)
|
111
|
+
)
|
112
|
+
self._expectedValues[self._roughButton] = rough_value
|
113
|
+
else:
|
114
|
+
self._roughButton = None
|
115
|
+
|
116
|
+
# connect signal / slot
|
117
|
+
self._manualLE.textChanged.connect(self._updateButtonChecked)
|
118
|
+
self._manualLE.textChanged.connect(self.valueChanged)
|
119
|
+
# set up
|
120
|
+
self._updateButtonChecked(self._manualLE.text())
|
121
|
+
|
122
|
+
def _updateButtonChecked(self, text):
|
123
|
+
if text == "":
|
124
|
+
return
|
125
|
+
current_value = self._dtype(text)
|
126
|
+
for button, activation_value in self._expectedValues.items():
|
127
|
+
with block_signals(button):
|
128
|
+
button.setChecked(activation_value == current_value)
|
129
|
+
|
130
|
+
def getStepSize(self) -> Union[float, int]:
|
131
|
+
"""
|
132
|
+
|
133
|
+
:return: displacement shift defined
|
134
|
+
:rtype: float
|
135
|
+
"""
|
136
|
+
return self._dtype(self._manualLE.text())
|
137
|
+
|
138
|
+
def setStepSize(self, value: Union[float, int]):
|
139
|
+
"""
|
140
|
+
|
141
|
+
:param float value: shift step
|
142
|
+
"""
|
143
|
+
assert isinstance(value, self._dtype)
|
144
|
+
self._manualLE.setText(str(value))
|
tomwer/gui/utils/unitsystem.py
CHANGED
@@ -35,7 +35,7 @@ __date__ = "21/09/2018"
|
|
35
35
|
import logging
|
36
36
|
|
37
37
|
from silx.gui import qt
|
38
|
-
from
|
38
|
+
from pyunitsystem import metricsystem
|
39
39
|
|
40
40
|
from tomwer.core.utils.char import MU_CHAR
|
41
41
|
|
@@ -64,10 +64,7 @@ class PixelEntry(qt.QWidget):
|
|
64
64
|
self.layout().addWidget(self._qlePixelSize)
|
65
65
|
|
66
66
|
# connect signal / slot
|
67
|
-
self._qlePixelSize.editingFinished.connect(self.
|
68
|
-
|
69
|
-
def _callbackEditingFinished(self):
|
70
|
-
self.valueChanged.emit()
|
67
|
+
self._qlePixelSize.editingFinished.connect(self.valueChanged)
|
71
68
|
|
72
69
|
def getValue(self):
|
73
70
|
if self._qlePixelSize.text() in ("unknown", ""):
|
tomwer/gui/utils/vignettes.py
CHANGED
@@ -36,7 +36,7 @@ __date__ = "26/02/2021"
|
|
36
36
|
|
37
37
|
|
38
38
|
import logging
|
39
|
-
from typing import Iterable, Union
|
39
|
+
from typing import Iterable, Union, Optional
|
40
40
|
|
41
41
|
import numpy
|
42
42
|
from silx.gui import qt
|
@@ -44,8 +44,9 @@ from silx.gui.plot import PlotWindow
|
|
44
44
|
from silx.gui.plot.utils.axis import SyncAxes
|
45
45
|
from silx.io.url import DataUrl
|
46
46
|
from tomoscan.esrf.scan.utils import get_data
|
47
|
+
from math import floor
|
47
48
|
|
48
|
-
from tomwer.core.process.reconstruction.scores.scores import ComputedScore
|
49
|
+
from tomwer.core.process.reconstruction.scores.scores import ComputedScore, ScoreMethod
|
49
50
|
|
50
51
|
_logger = logging.getLogger(__name__)
|
51
52
|
|
@@ -55,6 +56,12 @@ class VignettesQDialog(qt.QDialog):
|
|
55
56
|
|
56
57
|
SIZE_HINT = qt.QSize(820, 820)
|
57
58
|
|
59
|
+
AUTO_NB_COLUMN = "auto"
|
60
|
+
|
61
|
+
COLUMN_VALUES = (AUTO_NB_COLUMN, 1, 2, 3, 4, 5, 6, 8, 10)
|
62
|
+
|
63
|
+
RESIZE_MAX_TIME = 500
|
64
|
+
|
58
65
|
def __init__(
|
59
66
|
self,
|
60
67
|
value_name,
|
@@ -65,10 +72,24 @@ class VignettesQDialog(qt.QDialog):
|
|
65
72
|
colormap=None,
|
66
73
|
):
|
67
74
|
qt.QDialog.__init__(self, parent)
|
75
|
+
self.setWindowFlags(qt.Qt.Widget)
|
68
76
|
self._scan = None
|
69
77
|
self._selectedValue = None
|
70
78
|
self.setLayout(qt.QVBoxLayout())
|
79
|
+
|
80
|
+
self._nbColumnCB = qt.QComboBox(self)
|
81
|
+
for nb_column in self.COLUMN_VALUES:
|
82
|
+
self._nbColumnCB.addItem(str(nb_column))
|
83
|
+
|
84
|
+
self._columnWidget = qt.QWidget(self)
|
85
|
+
# needed intermediary widget because the 'setWidgetResizable' fails with QFormLayout on PyQt 5.14.1 and will want to keep compatiblity for now
|
86
|
+
self._columnWidget.setLayout(qt.QFormLayout())
|
87
|
+
self._columnWidget.layout().addRow("number of column", self._nbColumnCB)
|
88
|
+
self.layout().addWidget(self._columnWidget)
|
89
|
+
|
71
90
|
self._mainWidget = qt.QScrollArea(self)
|
91
|
+
self._mainWidget.setHorizontalScrollBarPolicy(qt.Qt.ScrollBarAlwaysOff)
|
92
|
+
self._mainWidget.setVerticalScrollBarPolicy(qt.Qt.ScrollBarAsNeeded)
|
72
93
|
self._vignettesWidget = VignettesWidget(
|
73
94
|
self,
|
74
95
|
with_spacer=True,
|
@@ -86,18 +107,30 @@ class VignettesQDialog(qt.QDialog):
|
|
86
107
|
types = qt.QDialogButtonBox.Ok | qt.QDialogButtonBox.Cancel
|
87
108
|
self._buttons = qt.QDialogButtonBox(parent=self)
|
88
109
|
self._buttons.setStandardButtons(types)
|
110
|
+
self._buttons.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum)
|
89
111
|
self.layout().addWidget(self._buttons)
|
90
112
|
|
91
113
|
# connect signal slot
|
92
114
|
self._buttons.button(qt.QDialogButtonBox.Ok).clicked.connect(self.accept)
|
93
|
-
|
94
115
|
self._buttons.button(qt.QDialogButtonBox.Cancel).clicked.connect(self.reject)
|
116
|
+
self._nbColumnCB.currentIndexChanged.connect(self._updateNbColumn)
|
117
|
+
|
118
|
+
self._resizeCallback = None
|
119
|
+
self._firstResizeEvent = True
|
95
120
|
|
96
121
|
def sizeHint(self):
|
97
122
|
"""Return a reasonable default size for usage in :class:`PlotWindow`"""
|
98
123
|
return self.SIZE_HINT
|
99
124
|
|
100
|
-
def setScores(self, scores, score_method):
|
125
|
+
def setScores(self, scores: dict, score_method: ScoreMethod):
|
126
|
+
"""
|
127
|
+
Expect a dictionary with possible values to select as key and
|
128
|
+
(2D numpy.array, score) as value.
|
129
|
+
Where the 2D numpy.array is the frame to display and the score if the
|
130
|
+
"indicator" score to display with the frame.
|
131
|
+
:param dict scores: with score as key and a tuple (url|numpy array, ComputedScore) as value
|
132
|
+
:param ScoreMethod score_method: score kind to be display
|
133
|
+
"""
|
101
134
|
self._vignettesWidget.setScores(scores=scores, score_method=score_method)
|
102
135
|
|
103
136
|
def selectedValue(self):
|
@@ -116,6 +149,55 @@ class VignettesQDialog(qt.QDialog):
|
|
116
149
|
self._vignettesWidget.close()
|
117
150
|
super().reject()
|
118
151
|
|
152
|
+
def getNColumn(self) -> Optional[int]:
|
153
|
+
n_column = self._nbColumnCB.currentText()
|
154
|
+
if n_column == self.AUTO_NB_COLUMN:
|
155
|
+
return None
|
156
|
+
else:
|
157
|
+
return int(n_column)
|
158
|
+
|
159
|
+
def setNbColumn(self, value: Union[int, str]):
|
160
|
+
if value not in self.COLUMN_VALUES:
|
161
|
+
raise ValueError(
|
162
|
+
f"Unhandled number of column requested ({value}). Valid values are {self.COLUMN_VALUES}"
|
163
|
+
)
|
164
|
+
idx = self._nbColumnCB.findText(str(value))
|
165
|
+
self._nbColumnCB.setCurrentIndex(idx)
|
166
|
+
|
167
|
+
@staticmethod
|
168
|
+
def computeOptimalNColumn(window_width, vignette_width):
|
169
|
+
n = floor(window_width / vignette_width)
|
170
|
+
# To check with Jerome; He seems to have work on the topic.
|
171
|
+
# might have some method for a smart way to do it
|
172
|
+
return max(1, n)
|
173
|
+
|
174
|
+
def _updateNbColumn(self, force_update=False):
|
175
|
+
n_colum = self.getNColumn()
|
176
|
+
if n_colum is None:
|
177
|
+
n_colum = self.computeOptimalNColumn(
|
178
|
+
window_width=self.width(),
|
179
|
+
vignette_width=400,
|
180
|
+
)
|
181
|
+
self._vignettesWidget.setNElementsPerRow(n_colum, force_update=force_update)
|
182
|
+
|
183
|
+
def resizeEvent(self, event):
|
184
|
+
if self._firstResizeEvent:
|
185
|
+
# cheap way to filter the first resize and avoid updating it
|
186
|
+
self._firstResizeEvent = False
|
187
|
+
else:
|
188
|
+
# print("resize event received...", event)
|
189
|
+
if self._resizeCallback is not None:
|
190
|
+
self._resizeCallback.timeout.disconnect(self._updateNbColumn)
|
191
|
+
self._resizeCallback.stop()
|
192
|
+
self._resizeCallback.deleteLater()
|
193
|
+
self._resizeCallback = None
|
194
|
+
self._resizeCallback = qt.QTimer(self)
|
195
|
+
self._resizeCallback.setSingleShot(True)
|
196
|
+
self._resizeCallback.timeout.connect(self._updateNbColumn)
|
197
|
+
self._resizeCallback.start(self.RESIZE_MAX_TIME)
|
198
|
+
|
199
|
+
super().resizeEvent(event)
|
200
|
+
|
119
201
|
|
120
202
|
class VignettesWidget(qt.QWidget):
|
121
203
|
"""
|
@@ -132,6 +214,7 @@ class VignettesWidget(qt.QWidget):
|
|
132
214
|
"""
|
133
215
|
|
134
216
|
DEFAULT_PLOT_PER_ROW = 2
|
217
|
+
MAX_NB_COLUMN = 999999
|
135
218
|
|
136
219
|
def __init__(
|
137
220
|
self,
|
@@ -155,6 +238,9 @@ class VignettesWidget(qt.QWidget):
|
|
155
238
|
self._scoreFormat = score_format
|
156
239
|
self._colormap = colormap
|
157
240
|
self._vignettes = []
|
241
|
+
self.__score_method = None
|
242
|
+
|
243
|
+
self.setLayout(qt.QGridLayout())
|
158
244
|
|
159
245
|
def close(self):
|
160
246
|
for vignette in self._vignettes:
|
@@ -164,24 +250,81 @@ class VignettesWidget(qt.QWidget):
|
|
164
250
|
def selectedValue(self):
|
165
251
|
sel_vignette = self._vignettesGroup.checkedButton()
|
166
252
|
if sel_vignette is not None:
|
167
|
-
return sel_vignette.
|
253
|
+
return sel_vignette.getScoreValue()
|
168
254
|
else:
|
169
255
|
return None
|
170
256
|
|
171
|
-
def setNElementsPerRow(self, n: int):
|
172
|
-
|
257
|
+
def setNElementsPerRow(self, n: int, force_update):
|
258
|
+
assert isinstance(n, int), "number of column must be an int"
|
259
|
+
if self._nPlotPerRow != n:
|
260
|
+
self._nPlotPerRow = n
|
261
|
+
self.__update()
|
262
|
+
elif force_update:
|
263
|
+
self.__update()
|
264
|
+
|
265
|
+
def __update(self):
|
266
|
+
scores, score_method = self.getScores()
|
267
|
+
self.setScores(scores, score_method)
|
268
|
+
# raise NotImplementedError
|
269
|
+
|
270
|
+
def getNElementsPerRow(self):
|
271
|
+
return self._nPlotPerRow
|
272
|
+
|
273
|
+
def getScores(self) -> tuple:
|
274
|
+
"""
|
275
|
+
Return currently displayed scores as (scores, scores_method)
|
276
|
+
"""
|
277
|
+
scores = {}
|
278
|
+
scores_method = self.__score_method
|
279
|
+
for vignette in self._vignettes:
|
280
|
+
assert isinstance(vignette, Vignette)
|
281
|
+
if scores_method in (ScoreMethod.STD, ScoreMethod.STD_INVERSE):
|
282
|
+
compute_score_args = {
|
283
|
+
"std": vignette.getScoreValue(),
|
284
|
+
"tv": None,
|
285
|
+
}
|
286
|
+
elif scores_method in (ScoreMethod.TV, ScoreMethod.TV_INVERSE):
|
287
|
+
compute_score_args = {
|
288
|
+
"std": None,
|
289
|
+
"tv": vignette.getScoreValue(),
|
290
|
+
}
|
291
|
+
elif scores_method in (ScoreMethod.TOMO_CONSISTENCY):
|
292
|
+
compute_score_args = {
|
293
|
+
"std": None,
|
294
|
+
"tv": None,
|
295
|
+
"tomo_consitency": vignette.getScoreValue(),
|
296
|
+
}
|
297
|
+
else:
|
298
|
+
raise ValueError(f"score method unhandled: {scores_method.value}")
|
299
|
+
|
300
|
+
scores[vignette.getValue()] = (
|
301
|
+
vignette.getData(),
|
302
|
+
ComputedScore(**compute_score_args),
|
303
|
+
)
|
304
|
+
return scores, scores_method
|
305
|
+
|
306
|
+
def clearVignettes(self):
|
307
|
+
for vignette in self._vignettes:
|
308
|
+
self.layout().removeWidget(vignette)
|
309
|
+
vignette.setParent(None)
|
310
|
+
self._vignettesGroup.removeButton(vignette)
|
311
|
+
# vignette.deleteLater()
|
312
|
+
self._vignettes = []
|
173
313
|
|
174
|
-
def setScores(self, scores: dict, score_method):
|
314
|
+
def setScores(self, scores: dict, score_method: ScoreMethod):
|
175
315
|
"""
|
176
316
|
Expect a dictionary with possible values to select as key and
|
177
317
|
(2D numpy.array, score) as value.
|
178
318
|
Where the 2D numpy.array is the frame to display and the score if the
|
179
319
|
"indicator" score to display with the frame.
|
180
|
-
:param dict scores: with score as key and
|
320
|
+
:param dict scores: with score as key and a tuple (url|numpy array, ComputedScore) as value
|
321
|
+
:param ScoreMethod score_method: score kind to be display
|
181
322
|
"""
|
182
323
|
if len(scores) < 1:
|
183
324
|
return
|
184
|
-
|
325
|
+
|
326
|
+
self.clearVignettes()
|
327
|
+
|
185
328
|
i_row = 0
|
186
329
|
scores_values = []
|
187
330
|
for i_score, (value, (data, score_cls)) in enumerate(scores.items()):
|
@@ -190,6 +333,7 @@ class VignettesWidget(qt.QWidget):
|
|
190
333
|
f"score is expected to be a dict with values as (v1: numpy.ndarray, v2: ComputedScore). v2 type Found: {type(score_cls)}"
|
191
334
|
)
|
192
335
|
scores_values.append(score_cls.get(score_method))
|
336
|
+
self.__score_method = ScoreMethod.from_value(score_method)
|
193
337
|
highest_score_indices = numpy.nanargmax(scores_values)
|
194
338
|
self._vignettesGroup = qt.QButtonGroup(self)
|
195
339
|
self._vignettesGroup.setExclusive(True)
|
@@ -202,10 +346,9 @@ class VignettesWidget(qt.QWidget):
|
|
202
346
|
|
203
347
|
for i_score, (value, (data, score_cls)) in enumerate(scores.items()):
|
204
348
|
score = score_cls.get(score_method)
|
205
|
-
i_column = i_score % self.
|
349
|
+
i_column = i_score % self.getNElementsPerRow()
|
206
350
|
# TODO: instead of having a binary color we could use
|
207
|
-
# colormap
|
208
|
-
# TODO: synchronize zooms
|
351
|
+
# colormap from green (good score) to red (bad score score)
|
209
352
|
if i_score == highest_score_indices or i_score in highest_score_indices:
|
210
353
|
frame_color = qt.Qt.green
|
211
354
|
else:
|
@@ -227,7 +370,7 @@ class VignettesWidget(qt.QWidget):
|
|
227
370
|
yAxis.append(widget.getPlotWidget().getYAxis())
|
228
371
|
|
229
372
|
self.layout().addWidget(widget, i_row, i_column)
|
230
|
-
if i_column == self.
|
373
|
+
if i_column == self.getNElementsPerRow() - 1:
|
231
374
|
i_row += 1
|
232
375
|
if i_score == 0:
|
233
376
|
# we cannot request all widget to keep the aspect ratio
|
@@ -238,7 +381,8 @@ class VignettesWidget(qt.QWidget):
|
|
238
381
|
if self._withSpacer:
|
239
382
|
spacer = qt.QWidget(self)
|
240
383
|
spacer.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Expanding)
|
241
|
-
|
384
|
+
# to simplify we add a spacer at the end. We do not expect to have more than 999999 column
|
385
|
+
self.layout().addWidget(spacer, i_row + 1, self.MAX_NB_COLUMN)
|
242
386
|
|
243
387
|
# constrain axis synchronization
|
244
388
|
self.__constraintXAxis = SyncAxes(
|
@@ -339,6 +483,18 @@ class Vignette(qt.QToolButton):
|
|
339
483
|
def getValue(self):
|
340
484
|
return self._value
|
341
485
|
|
486
|
+
def getData(self) -> numpy.ndarray:
|
487
|
+
return self._plot.getImage().getData()
|
488
|
+
|
489
|
+
def getScoreValue(self):
|
490
|
+
return self._valueLabel.score
|
491
|
+
|
492
|
+
def getScoreName(self):
|
493
|
+
return self._scoreName
|
494
|
+
|
495
|
+
def getValueName(self):
|
496
|
+
return self._valueName
|
497
|
+
|
342
498
|
def paintEvent(self, event):
|
343
499
|
super().paintEvent(event)
|
344
500
|
painter = qt.QPainter(self)
|
@@ -375,6 +531,7 @@ class ValueLabel(qt.QWidget):
|
|
375
531
|
self, parent, value, score, value_name, score_name, value_format, score_format
|
376
532
|
):
|
377
533
|
qt.QWidget.__init__(self, parent)
|
534
|
+
self._score = score
|
378
535
|
self.setLayout(qt.QHBoxLayout())
|
379
536
|
if value_format is not None:
|
380
537
|
str_value = value_format.format(value)
|
@@ -390,3 +547,7 @@ class ValueLabel(qt.QWidget):
|
|
390
547
|
txt = f"({score_name}: {str_score})"
|
391
548
|
self._scoreLabel = qt.QLabel(txt, self)
|
392
549
|
self.layout().addWidget(self._scoreLabel)
|
550
|
+
|
551
|
+
@property
|
552
|
+
def score(self):
|
553
|
+
return self._score
|
@@ -31,6 +31,7 @@ import logging
|
|
31
31
|
import weakref
|
32
32
|
|
33
33
|
from silx.gui import qt
|
34
|
+
from silx.gui.dialog.ColormapDialog import DisplayMode
|
34
35
|
from silx.gui.plot.ImageStack import ImageStack as _ImageStack
|
35
36
|
from silx.gui.plot.ImageStack import UrlLoader
|
36
37
|
from silx.gui.utils.signal import SignalProxy
|
@@ -54,12 +55,6 @@ except ImportError: # pragma: no cover
|
|
54
55
|
has_PIL = False # pragma: no cover
|
55
56
|
else:
|
56
57
|
has_PIL = True
|
57
|
-
try:
|
58
|
-
import cv2 # pragma: no cover
|
59
|
-
except ImportError: # pragma: no cover
|
60
|
-
has_cv2 = False # pragma: no cover
|
61
|
-
else:
|
62
|
-
has_cv2 = True
|
63
58
|
import os
|
64
59
|
import time
|
65
60
|
|
@@ -85,6 +80,7 @@ class DataViewer(qt.QMainWindow):
|
|
85
80
|
self._viewer = ImageStack(
|
86
81
|
parent=self, show_overview=show_overview, backend=backend
|
87
82
|
)
|
83
|
+
self._viewer.getPlotWidget().getMaskAction().setVisible(False)
|
88
84
|
self._viewer.getPlotWidget().setYAxisInverted(Y_AXIS_DOWNWARD)
|
89
85
|
# set an UrlLoader managing .npy and .vol
|
90
86
|
self._viewer.getPlotWidget().setKeepDataAspectRatio(True)
|
@@ -112,7 +108,7 @@ class DataViewer(qt.QMainWindow):
|
|
112
108
|
|
113
109
|
# connect signal / slot
|
114
110
|
self._controls.sigDisplayModeChanged.connect(self._updateDisplay)
|
115
|
-
self._controls.sigDisplayModeChanged.connect(self.
|
111
|
+
self._controls.sigDisplayModeChanged.connect(self.sigConfigChanged)
|
116
112
|
|
117
113
|
def getPlotWidget(self):
|
118
114
|
return self._viewer.getPlotWidget()
|
@@ -129,9 +125,6 @@ class DataViewer(qt.QMainWindow):
|
|
129
125
|
def setScanOverviewVisible(self, visible: bool) -> None:
|
130
126
|
self._viewer.setScanOverviewVisible(visible=visible)
|
131
127
|
|
132
|
-
def cleanBeforeQuit(self):
|
133
|
-
self._viewer._plot.updateThread.stop()
|
134
|
-
|
135
128
|
def getUrlListDockWidget(self):
|
136
129
|
return self._viewer.getUrlListDockWidget()
|
137
130
|
|
@@ -259,9 +252,6 @@ class DataViewer(qt.QMainWindow):
|
|
259
252
|
else:
|
260
253
|
self._viewer.reset()
|
261
254
|
|
262
|
-
def _reportSettingsUpdate(self):
|
263
|
-
self.sigConfigChanged.emit()
|
264
|
-
|
265
255
|
def clear(self):
|
266
256
|
self._scan = None
|
267
257
|
self._viewer.reset()
|
@@ -421,14 +411,27 @@ class DisplayControl(qt.QWidget):
|
|
421
411
|
|
422
412
|
|
423
413
|
class ImageStack(_ImageStack):
|
414
|
+
"""
|
415
|
+
Image stack dedicated to data display.
|
416
|
+
|
417
|
+
It deal for example with data normalization...
|
418
|
+
"""
|
419
|
+
|
424
420
|
def __init__(self, parent, show_overview=True, backend=None):
|
425
421
|
self._normFct = None
|
426
422
|
self._url_indexes = None
|
427
423
|
super().__init__(parent)
|
428
424
|
self.getPlotWidget().setBackend(backend)
|
425
|
+
|
426
|
+
# tune colormap dialog to have histogram by default
|
427
|
+
colormapAction = self.getPlotWidget().getColormapAction()
|
428
|
+
colormapDialog = colormapAction.getColormapDialog()
|
429
|
+
colormapDialog.getHistogramWidget().setDisplayMode(DisplayMode.HISTOGRAM)
|
430
|
+
colormapAction.setColormapDialog(colormapDialog)
|
431
|
+
|
429
432
|
self.setUrlLoaderClass(_TomwerUrlLoader)
|
430
433
|
# hide axis to be display
|
431
|
-
self._plot.
|
434
|
+
self._plot.setAxesDisplayed(False)
|
432
435
|
self._loadSliceParams = False
|
433
436
|
self._resetZoom = True
|
434
437
|
|
@@ -493,17 +496,15 @@ class ImageStack(_ImageStack):
|
|
493
496
|
return self._tableDockWidget
|
494
497
|
|
495
498
|
def resetZoom(self):
|
496
|
-
self._plot.
|
499
|
+
self._plot.resetZoom()
|
497
500
|
|
498
501
|
def setLimits(self, x_min, x_max, y_min, y_max):
|
499
|
-
|
500
|
-
plot.setLimits(xmin=x_min, ymin=y_min, xmax=x_max, ymax=y_max)
|
502
|
+
self._plot.setLimits(xmin=x_min, ymin=y_min, xmax=x_max, ymax=y_max)
|
501
503
|
|
502
504
|
def getLimits(self):
|
503
|
-
plot = self._plot.getPlotWidget()
|
504
505
|
limits = []
|
505
|
-
limits.extend(
|
506
|
-
limits.extend(
|
506
|
+
limits.extend(self._plot.getGraphXLimits())
|
507
|
+
limits.extend(self._plot.getGraphYLimits())
|
507
508
|
return tuple(limits)
|
508
509
|
|
509
510
|
def setSliceReconsParamsVisible(self, visible):
|
@@ -535,8 +536,19 @@ class ImageStack(_ImageStack):
|
|
535
536
|
return
|
536
537
|
|
537
538
|
if data.ndim != 2:
|
538
|
-
if data.ndim == 3
|
539
|
-
|
539
|
+
if data.ndim == 3:
|
540
|
+
if data.shape[0] == 1:
|
541
|
+
# if reconstruction along z
|
542
|
+
data = data.reshape((data.shape[1], data.shape[2]))
|
543
|
+
elif data.shape[1] == 1:
|
544
|
+
# if reconstruction along y
|
545
|
+
data = data.reshape((data.shape[0], data.shape[2]))
|
546
|
+
elif data.shape[2] == 1:
|
547
|
+
# if reconstruction along z
|
548
|
+
data = data.reshape((data.shape[0], data.shape[1]))
|
549
|
+
else:
|
550
|
+
_logger.warning(f"Image Stack only manage 2D data. Url: {url}")
|
551
|
+
return
|
540
552
|
else:
|
541
553
|
_logger.warning(f"Image Stack only manage 2D data. Url: {url}")
|
542
554
|
return
|
@@ -547,10 +559,14 @@ class ImageStack(_ImageStack):
|
|
547
559
|
self._urlData[url] = norm_data
|
548
560
|
|
549
561
|
if self.getCurrentUrl().path() == url:
|
550
|
-
self._plot.
|
562
|
+
self._plot.addImage(self._urlData[url])
|
563
|
+
if hasattr(self, "getWaiterOverlay"):
|
564
|
+
self.getWaiterOverlay().hide()
|
565
|
+
else:
|
566
|
+
self._waitingOverlay.hide()
|
551
567
|
if self._resetZoom:
|
552
568
|
self._resetZoom = False
|
553
|
-
self._plot.
|
569
|
+
self._plot.resetZoom()
|
554
570
|
|
555
571
|
if sender in self._loadingThreads:
|
556
572
|
self._loadingThreads.remove(sender)
|
@@ -560,11 +576,16 @@ class ImageStack(_ImageStack):
|
|
560
576
|
self._resetZoom = reset
|
561
577
|
|
562
578
|
def setCurrentUrl(self, url: DataUrl):
|
563
|
-
if
|
579
|
+
if url in ("", None):
|
580
|
+
url = None
|
581
|
+
elif isinstance(url, str):
|
564
582
|
url = DataUrl(path=url)
|
565
583
|
elif not isinstance(url, DataUrl):
|
566
584
|
raise TypeError
|
567
|
-
if
|
585
|
+
if url is None:
|
586
|
+
pass
|
587
|
+
# FIXME: add a function to clear the metadata widget
|
588
|
+
elif self._loadSliceParams:
|
568
589
|
try:
|
569
590
|
self._reconsWidget.setVolumeMetadata(
|
570
591
|
self._metadatas.get(url.path(), None)
|
@@ -575,7 +596,7 @@ class ImageStack(_ImageStack):
|
|
575
596
|
|
576
597
|
def setUrls(self, urls: list):
|
577
598
|
_ImageStack.setUrls(self, urls)
|
578
|
-
listWidget = self._urlsTable._urlsTable
|
599
|
+
listWidget = self._urlsTable._urlsTable
|
579
600
|
items = []
|
580
601
|
for i in range(listWidget.count()):
|
581
602
|
# TODO: do this on the fly
|
@@ -607,14 +628,6 @@ class _TomwerUrlLoader(UrlLoader):
|
|
607
628
|
def run(self):
|
608
629
|
if self.url.file_path().endswith(".vol"):
|
609
630
|
self.data = self._load_vol()
|
610
|
-
elif self.url.scheme() == "cv2":
|
611
|
-
if has_cv2:
|
612
|
-
self.data = cv2.imread(self.url.file_path(), -1)
|
613
|
-
else:
|
614
|
-
self.data = None
|
615
|
-
_logger.warning(
|
616
|
-
f"need to install cv2 to read cast file {self.url.file_path()} because fabio fails to read float16"
|
617
|
-
)
|
618
631
|
elif self.url.scheme() == "tomwer":
|
619
632
|
if has_PIL:
|
620
633
|
self.data = numpy.array(Image.open(self.url.file_path()))
|