tomwer 1.0.3__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- orangecontrib/tomwer/tutorials/EBS_tomo_listener.ows +39 -0
- orangecontrib/tomwer/tutorials/cast_volume.ows +34 -0
- orangecontrib/tomwer/tutorials/simple_slice_reconstruction.ows +39 -0
- orangecontrib/tomwer/tutorials/simple_volume_local_reconstruction.ows +49 -0
- orangecontrib/tomwer/tutorials/simple_volume_to_slurm_reconstruction.ows +59 -0
- orangecontrib/tomwer/tutorials/using_saaxis_to_find_cor.ows +44 -0
- orangecontrib/tomwer/widgets/cluster/FutureSupervisorOW.py +1 -1
- orangecontrib/tomwer/widgets/cluster/SlurmClusterOW.py +14 -4
- orangecontrib/tomwer/widgets/cluster/__init__.py +1 -1
- orangecontrib/tomwer/widgets/control/DataListOW.py +12 -5
- orangecontrib/tomwer/widgets/control/DataListenerOW.py +18 -9
- orangecontrib/tomwer/widgets/control/DataSelectorOW.py +13 -6
- orangecontrib/tomwer/widgets/control/DataTransfertOW.py +3 -5
- orangecontrib/tomwer/widgets/control/DataValidatorOW.py +8 -4
- orangecontrib/tomwer/widgets/control/DataWatcherOW.py +4 -6
- orangecontrib/tomwer/widgets/control/EDF2NXTomomillOW.py +49 -62
- orangecontrib/tomwer/widgets/control/FilterOW.py +2 -4
- orangecontrib/tomwer/widgets/control/NXTomomillMixIn.py +93 -0
- orangecontrib/tomwer/widgets/control/NXTomomillOW.py +135 -129
- orangecontrib/tomwer/widgets/control/NotifierOW.py +34 -9
- orangecontrib/tomwer/widgets/control/SingleTomoObjOW.py +3 -5
- orangecontrib/tomwer/widgets/control/TomoObjSerieOW.py +19 -13
- orangecontrib/tomwer/widgets/control/VolumeSelector.py +12 -4
- orangecontrib/tomwer/widgets/control/VolumeSymLinkOW.py +11 -7
- orangecontrib/tomwer/widgets/control/icons/notification.svg +4 -4
- orangecontrib/tomwer/widgets/control/icons/nxtomomill.png +0 -0
- orangecontrib/tomwer/widgets/control/icons/nxtomomill.svg +8 -5
- orangecontrib/tomwer/widgets/control/icons/tomoobjserie.png +0 -0
- orangecontrib/tomwer/widgets/control/icons/tomoobjserie.svg +73 -78
- orangecontrib/tomwer/widgets/edit/DarkFlatPatchOW.py +16 -4
- orangecontrib/tomwer/widgets/edit/NXtomoEditorOW.py +100 -0
- orangecontrib/tomwer/widgets/edit/icons/image_key_editor.png +0 -0
- orangecontrib/tomwer/widgets/edit/icons/image_key_upgrader.png +0 -0
- orangecontrib/tomwer/widgets/edit/icons/nx_tomo_editor.png +0 -0
- orangecontrib/tomwer/widgets/edit/icons/nx_tomo_editor.svg +123 -0
- orangecontrib/tomwer/widgets/edit/test/test_dark_flat_patch.py +21 -1
- orangecontrib/tomwer/widgets/edit/test/test_image_key_editor.py +1 -1
- orangecontrib/tomwer/widgets/edit/test/test_image_key_upgrader.py +1 -1
- orangecontrib/tomwer/widgets/edit/test/test_nxtomo_editor.py +25 -0
- orangecontrib/tomwer/widgets/other/PythonScriptOW.py +19 -11
- orangecontrib/tomwer/widgets/reconstruction/AxisOW.py +20 -14
- orangecontrib/tomwer/widgets/reconstruction/CastNabuVolumeOW.py +24 -10
- orangecontrib/tomwer/widgets/reconstruction/DarkRefAndCopyOW.py +26 -21
- orangecontrib/tomwer/widgets/reconstruction/NabuOW.py +29 -12
- orangecontrib/tomwer/widgets/reconstruction/NabuVolumeOW.py +44 -17
- orangecontrib/tomwer/widgets/reconstruction/SAAxisOW.py +28 -20
- orangecontrib/tomwer/widgets/reconstruction/SADeltaBetaOW.py +24 -18
- orangecontrib/tomwer/widgets/reconstruction/SinoNormOW.py +6 -6
- orangecontrib/tomwer/widgets/reconstruction/TofuOW.py +4 -2
- orangecontrib/tomwer/widgets/reconstruction/icons/nabu_2d.png +0 -0
- orangecontrib/tomwer/widgets/reconstruction/icons/nabu_2d.svg +11 -8
- orangecontrib/tomwer/widgets/visualization/DataViewerOW.py +10 -4
- orangecontrib/tomwer/widgets/visualization/DiffViewerOW.py +1 -1
- orangecontrib/tomwer/widgets/visualization/NXtomoMetadataViewerOW.py +69 -0
- orangecontrib/tomwer/widgets/visualization/SampleMovedOW.py +2 -4
- orangecontrib/tomwer/widgets/visualization/icons/nx_tomo_metadata_viewer.png +0 -0
- orangecontrib/tomwer/widgets/visualization/icons/nx_tomo_metadata_viewer.svg +105 -0
- tomwer/__main__.py +10 -5
- tomwer/app/canvas_launcher/config.py +10 -10
- tomwer/app/canvas_launcher/mainwindow.py +68 -6
- tomwer/app/canvas_launcher/widgetsscheme.py +1 -3
- tomwer/app/darkref.py +16 -12
- tomwer/app/imagekeyeditor.py +2 -2
- tomwer/app/imagekeyupgrader.py +104 -0
- tomwer/app/intensitynormalization.py +0 -1
- tomwer/app/nxtomoeditor.py +103 -0
- tomwer/app/rsync.py +1 -1
- tomwer/core/cluster/cluster.py +1 -1
- tomwer/core/futureobject.py +1 -0
- tomwer/core/process/control/datalistener/datalistener.py +7 -1
- tomwer/core/process/control/datalistener/rpcserver.py +3 -4
- tomwer/core/process/control/datawatcher/datawatcher.py +18 -18
- tomwer/core/process/control/datawatcher/datawatcherobserver.py +5 -8
- tomwer/core/process/control/datawatcher/datawatcherprocess.py +2 -3
- tomwer/core/process/control/datawatcher/edfdwprocess.py +2 -2
- tomwer/core/process/control/nxtomomill.py +33 -58
- tomwer/core/process/control/scanlist.py +2 -1
- tomwer/core/process/control/scanselector.py +7 -0
- tomwer/core/process/control/scantransfer.py +2 -2
- tomwer/core/process/control/scanvalidator.py +6 -5
- tomwer/core/process/control/singletomoobj.py +2 -1
- tomwer/core/process/control/timer.py +2 -1
- tomwer/core/process/control/tomoobjserie.py +8 -2
- tomwer/core/process/control/volumeselector.py +2 -1
- tomwer/core/process/control/volumesymlink.py +2 -1
- tomwer/core/process/edit/darkflatpatch.py +2 -1
- tomwer/core/process/edit/imagekeyeditor.py +4 -3
- tomwer/core/process/reconstruction/axis/axis.py +29 -32
- tomwer/core/process/reconstruction/axis/mode.py +3 -2
- tomwer/core/process/reconstruction/axis/params.py +35 -16
- tomwer/core/process/reconstruction/darkref/darkrefs.py +90 -707
- tomwer/core/process/reconstruction/darkref/darkrefscopy.py +44 -16
- tomwer/core/process/reconstruction/darkref/params.py +62 -67
- tomwer/core/process/reconstruction/lamino/tofu.py +1 -2
- tomwer/core/process/reconstruction/nabu/castvolume.py +21 -26
- tomwer/core/process/reconstruction/nabu/nabucommon.py +36 -38
- tomwer/core/process/reconstruction/nabu/nabuscores.py +28 -13
- tomwer/core/process/reconstruction/nabu/nabuslices.py +41 -14
- tomwer/core/process/reconstruction/nabu/nabuvolume.py +21 -12
- tomwer/core/process/reconstruction/nabu/utils.py +32 -3
- tomwer/core/process/reconstruction/normalization/normalization.py +9 -8
- tomwer/core/process/reconstruction/saaxis/saaxis.py +46 -20
- tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +38 -12
- tomwer/core/process/reconstruction/test/__init__.py +0 -39
- tomwer/core/process/reconstruction/test/test_axis_params.py +25 -3
- tomwer/core/process/reconstruction/test/test_darkref_copy.py +117 -1
- tomwer/core/process/script/python.py +16 -12
- tomwer/core/process/task.py +3 -7
- tomwer/core/process/test/test_axis.py +1 -1
- tomwer/core/process/test/test_dark_and_flat.py +41 -111
- tomwer/core/process/test/test_data_listener.py +0 -29
- tomwer/core/process/test/test_data_transfer.py +10 -14
- tomwer/core/process/test/test_nabu.py +1 -1
- tomwer/core/process/test/test_normalization.py +1 -1
- tomwer/core/process/visualization/liveslice.py +6 -0
- tomwer/core/scan/blissscan.py +37 -2
- tomwer/core/scan/edfscan.py +19 -8
- tomwer/core/scan/hdf5scan.py +10 -4
- tomwer/core/scan/scanbase.py +35 -29
- tomwer/core/scan/scanfactory.py +3 -17
- tomwer/core/scan/test/test_h5.py +1 -1
- tomwer/core/scan/test/test_process_registration.py +0 -11
- tomwer/core/scan/test/test_scan.py +32 -30
- tomwer/core/settings.py +2 -2
- tomwer/core/test/test_utils.py +1 -1
- tomwer/core/tomwer_object.py +19 -0
- tomwer/core/utils/__init__.py +0 -45
- tomwer/core/utils/char.py +2 -0
- tomwer/core/utils/gpu.py +5 -5
- tomwer/core/utils/nxtomoutils.py +2 -2
- tomwer/core/utils/scanutils.py +50 -0
- tomwer/core/utils/volumeutils.py +13 -0
- tomwer/core/volume/edfvolume.py +4 -0
- tomwer/core/volume/hdf5volume.py +4 -0
- tomwer/core/volume/jp2kvolume.py +4 -0
- tomwer/core/volume/rawvolume.py +22 -5
- tomwer/core/volume/tiffvolume.py +4 -0
- tomwer/core/volume/volumebase.py +19 -12
- tomwer/core/volume/volumefactory.py +20 -1
- tomwer/gui/cluster/slurm.py +1 -1
- tomwer/gui/cluster/supervisor.py +0 -2
- tomwer/gui/cluster/test/test_cluster.py +2 -2
- tomwer/gui/control/datalist.py +109 -36
- tomwer/gui/control/datatransfert.py +1 -1
- tomwer/gui/control/datawatcher/configuration.py +0 -2
- tomwer/gui/control/datawatcher/datawatcher.py +23 -13
- tomwer/gui/control/datawatcher/datawatcherobserver.py +1 -1
- tomwer/gui/control/observations.py +0 -3
- tomwer/gui/control/selectorwidgetbase.py +42 -12
- tomwer/gui/control/serie/seriecreator.py +967 -0
- tomwer/{web/__init__.py → gui/control/serie/seriewaiter.py} +5 -7
- tomwer/gui/control/singletomoobj.py +15 -4
- tomwer/gui/control/test/test_datalist.py +1 -1
- tomwer/gui/control/test/test_datalistener.py +1 -1
- tomwer/gui/control/test/test_inputwidget.py +1 -1
- tomwer/gui/control/test/test_process_manager.py +1 -13
- tomwer/gui/control/test/test_scanselector.py +1 -1
- tomwer/gui/control/test/test_scanvalidator.py +1 -1
- tomwer/gui/control/test/test_single_tomo_obj.py +1 -1
- tomwer/gui/control/test/test_volume_dialog.py +19 -7
- tomwer/gui/control/test/test_volumeselector.py +4 -4
- tomwer/gui/debugtools/datasetgenerator.py +1 -9
- tomwer/gui/edit/dkrfpatch.py +2 -3
- tomwer/gui/edit/imagekeyeditor.py +12 -11
- tomwer/gui/edit/nxtomoeditor.py +475 -0
- tomwer/gui/edit/test/test_dkrf_patch.py +2 -14
- tomwer/gui/edit/test/test_image_key_editor.py +2 -2
- tomwer/gui/edit/test/test_nx_editor.py +155 -0
- tomwer/gui/icons.py +0 -1
- tomwer/gui/qfolderdialog.py +11 -0
- tomwer/gui/reconstruction/axis/CompareImages.py +27 -29
- tomwer/gui/reconstruction/axis/axis.py +2 -0
- tomwer/gui/reconstruction/axis/radioaxis.py +70 -14
- tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +7 -9
- tomwer/gui/reconstruction/darkref/darkrefwidget.py +22 -24
- tomwer/gui/reconstruction/lamino/tofu/projections.py +1 -1
- tomwer/gui/reconstruction/lamino/tofu/tofu.py +3 -3
- tomwer/gui/reconstruction/lamino/tofu/tofuexpert.py +4 -4
- tomwer/gui/reconstruction/lamino/tofu/tofuoutput.py +10 -5
- tomwer/gui/reconstruction/nabu/castvolume.py +103 -24
- tomwer/gui/reconstruction/nabu/check.py +1 -1
- tomwer/gui/reconstruction/nabu/nabuconfig/ctf.py +352 -0
- tomwer/gui/reconstruction/nabu/nabuconfig/nabuconfig.py +0 -9
- tomwer/gui/reconstruction/nabu/nabuconfig/output.py +1 -1
- tomwer/gui/reconstruction/nabu/nabuconfig/phase.py +18 -19
- tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +30 -7
- tomwer/gui/reconstruction/nabu/nabuconfig/reconstruction.py +26 -15
- tomwer/gui/reconstruction/nabu/slices.py +10 -4
- tomwer/gui/reconstruction/nabu/slurm.py +1 -1
- tomwer/gui/reconstruction/nabu/volume.py +13 -7
- tomwer/gui/reconstruction/normalization/intensity.py +1 -5
- tomwer/gui/reconstruction/saaxis/corrangeselector.py +10 -37
- tomwer/gui/reconstruction/saaxis/saaxis.py +11 -7
- tomwer/gui/reconstruction/saaxis/sliceselector.py +11 -26
- tomwer/gui/reconstruction/sadeltabeta/saadeltabeta.py +13 -8
- tomwer/gui/reconstruction/scores/scoreplot.py +67 -62
- tomwer/gui/reconstruction/test/test_axis.py +2 -2
- tomwer/gui/reconstruction/test/test_lamino.py +2 -2
- tomwer/gui/reconstruction/test/test_nabu.py +14 -1
- tomwer/gui/reconstruction/test/test_saaxis.py +8 -17
- tomwer/gui/reconstruction/test/test_sadeltabeta.py +7 -13
- tomwer/gui/stackplot.py +11 -28
- tomwer/gui/test/test_axis_gui.py +4 -4
- tomwer/gui/test/test_qfolder_dialog.py +12 -0
- tomwer/gui/utils/inputwidget.py +42 -22
- tomwer/gui/utils/lineselector/lineselector.py +13 -21
- tomwer/gui/utils/scandescription.py +2 -4
- tomwer/gui/utils/slider.py +1 -102
- tomwer/gui/utils/unitsystem.py +48 -11
- tomwer/gui/visualization/dataviewer.py +24 -17
- tomwer/gui/visualization/diffviewer/diffviewer.py +2 -11
- tomwer/gui/visualization/nxtomometadata.py +21 -0
- tomwer/gui/visualization/scanoverview.py +0 -1
- tomwer/gui/visualization/test/test_nx_tomo_metadata_viewer.py +72 -0
- tomwer/gui/visualization/test/test_stacks.py +1 -1
- tomwer/gui/visualization/tomoobjoverview.py +49 -0
- tomwer/gui/visualization/volumeoverview.py +64 -0
- tomwer/gui/visualization/volumeviewer.py +1 -1
- tomwer/io/utils/utils.py +2 -2
- tomwer/resources/gui/icons/multi-document-save.png +0 -0
- tomwer/resources/gui/icons/multi-document-save.svg +101 -0
- tomwer/resources/gui/illustrations/ctf_z1.png +0 -0
- tomwer/resources/gui/illustrations/ctf_z1.svg +471 -0
- tomwer/synctools/axis.py +0 -1
- tomwer/synctools/darkref.py +0 -1
- tomwer/synctools/datalistener.py +5 -1
- tomwer/synctools/imageloaderthread.py +2 -2
- tomwer/synctools/saaxis.py +0 -1
- tomwer/synctools/sadeltabeta.py +0 -1
- tomwer/synctools/stacks/edit/imagekeyeditor.py +1 -1
- tomwer/synctools/stacks/processingstack.py +2 -2
- tomwer/synctools/stacks/reconstruction/castvolume.py +1 -0
- tomwer/synctools/stacks/reconstruction/dkrefcopy.py +1 -1
- tomwer/synctools/stacks/reconstruction/lamino.py +1 -3
- tomwer/synctools/stacks/reconstruction/sadeltabeta.py +0 -2
- tomwer/synctools/test/test_darkRefs.py +32 -149
- tomwer/synctools/test/test_foldertransfer.py +1 -1
- tomwer/synctools/test/test_scanstages.py +2 -2
- tomwer/tests/conftest.py +51 -0
- tomwer/{test → tests}/test_scripts.py +1 -1
- tomwer/tests/test_utils.py +10 -0
- tomwer/{test → tests}/utils/utilstest.py +0 -11
- tomwer/version.py +3 -3
- {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/METADATA +14 -16
- {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/RECORD +255 -235
- {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/WHEEL +1 -1
- {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/entry_points.txt +6 -0
- orangecontrib/tomwer/setup.py +0 -45
- orangecontrib/tomwer/widgets/setup.py +0 -49
- tomwer/app/process.py +0 -153
- tomwer/core/process/reconstruction/nabu/slurm.py +0 -36
- tomwer/core/process/reconstruction/utils/nabu_slice_exec.py +0 -10
- tomwer/core/utils/laminoutils.py +0 -80
- tomwer/gui/utils/lineselector/lineselection.py +0 -76
- tomwer/setup.py +0 -52
- tomwer/slurm/executor.py +0 -36
- tomwer/slurm/job.py +0 -349
- tomwer/slurm/utils.py +0 -44
- tomwer/web/client.py +0 -43
- tomwer/web/config.py +0 -36
- tomwer/web/test/test_graylog_connection.py +0 -59
- {tomwer/slurm → orangecontrib/tomwer/tutorials}/__init__.py +0 -0
- /tomwer/{test → gui/control/serie}/__init__.py +0 -0
- /tomwer/{web/test → tests}/__init__.py +0 -0
- /tomwer/{test → tests}/utils/__init__.py +0 -0
- /tomwer-1.0.3-py3.8-nspkg.pth → /tomwer-1.1.0-py3.9-nspkg.pth +0 -0
- {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/LICENSE +0 -0
- {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/namespace_packages.txt +0 -0
- {tomwer-1.0.3.dist-info → tomwer-1.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,475 @@
|
|
1
|
+
import weakref
|
2
|
+
import logging
|
3
|
+
import numpy
|
4
|
+
import h5py
|
5
|
+
from silx.gui import qt
|
6
|
+
from typing import Optional
|
7
|
+
from tomwer.core.scan.hdf5scan import HDF5TomoScan
|
8
|
+
from tomwer.gui.utils.unitsystem import MetricEntry
|
9
|
+
from tomwer.gui.utils.scandescription import ScanNameLabelAndShape
|
10
|
+
from tomoscan.esrf.scan.hdf5scan import ImageKey
|
11
|
+
from tomoscan.io import HDF5File
|
12
|
+
from tomoscan.nexus.paths.nxtomo import get_paths as get_nexus_paths
|
13
|
+
from silx.io.utils import h5py_read_dataset
|
14
|
+
from tomoscan.scanbase import _FOV
|
15
|
+
|
16
|
+
|
17
|
+
_logger = logging.getLogger(__name__)
|
18
|
+
|
19
|
+
|
20
|
+
class NXtomoEditorDialog(qt.QDialog):
|
21
|
+
def __init__(self, parent=None) -> None:
|
22
|
+
super().__init__(parent)
|
23
|
+
self.setLayout(qt.QVBoxLayout())
|
24
|
+
|
25
|
+
self.mainWidget = NXtomoEditor(parent=self)
|
26
|
+
self.layout().addWidget(self.mainWidget)
|
27
|
+
|
28
|
+
types = qt.QDialogButtonBox.Ok
|
29
|
+
self._buttons = qt.QDialogButtonBox(parent=self)
|
30
|
+
self._buttons.setStandardButtons(types)
|
31
|
+
self._buttons.button(qt.QDialogButtonBox.Ok).setText("validate")
|
32
|
+
self.layout().addWidget(self._buttons)
|
33
|
+
|
34
|
+
# expose API
|
35
|
+
def setScan(self, scan):
|
36
|
+
self.mainWidget.setScan(scan)
|
37
|
+
|
38
|
+
def overwriteNXtomo(self):
|
39
|
+
self.mainWidget.overwriteNXtomo()
|
40
|
+
|
41
|
+
|
42
|
+
class NXtomoEditor(qt.QWidget):
|
43
|
+
"""
|
44
|
+
class to edit parameter of a NXtomo.
|
45
|
+
The preliminary goal is to let the user define pixel / voxel position and x and z positions
|
46
|
+
in order to simplify stitching down the line
|
47
|
+
|
48
|
+
As energy and scan range was also often requested this part is also editable (user bonus ^^)
|
49
|
+
"""
|
50
|
+
|
51
|
+
def __init__(self, parent=None):
|
52
|
+
super().__init__(parent)
|
53
|
+
self._editableWidgets = []
|
54
|
+
self._scan = None
|
55
|
+
self.setLayout(qt.QVBoxLayout())
|
56
|
+
self._scanInfoQLE = ScanNameLabelAndShape(parent=self)
|
57
|
+
self.layout().addWidget(self._scanInfoQLE)
|
58
|
+
|
59
|
+
# nxtomo tree
|
60
|
+
self._tree = qt.QTreeWidget(self)
|
61
|
+
self._tree.setColumnCount(2)
|
62
|
+
self._tree.setHeaderLabels(("entry", "value"))
|
63
|
+
self.layout().addWidget(self._tree)
|
64
|
+
|
65
|
+
# 1: instrument
|
66
|
+
self._instrumentQTWI = qt.QTreeWidgetItem(self._tree)
|
67
|
+
self._instrumentQTWI.setText(0, "instrument")
|
68
|
+
# handle energy
|
69
|
+
self._beamQTWI = qt.QTreeWidgetItem(self._instrumentQTWI)
|
70
|
+
self._beamQTWI.setText(0, "beam")
|
71
|
+
self._energyQTWI = qt.QTreeWidgetItem(self._beamQTWI)
|
72
|
+
self._energyQTWI.setText(0, "energy (keV)")
|
73
|
+
self._energyQLE = EnergyEntry("", self)
|
74
|
+
self._energyQLE.setPlaceholderText("energy in kev")
|
75
|
+
self._tree.setItemWidget(self._energyQTWI, 1, self._energyQLE)
|
76
|
+
self._editableWidgets.append(self._energyQLE)
|
77
|
+
|
78
|
+
# 1.1 detector
|
79
|
+
self._detectorQTWI = qt.QTreeWidgetItem(self._instrumentQTWI)
|
80
|
+
self._detectorQTWI.setText(0, "detector")
|
81
|
+
## pixel size
|
82
|
+
self._xPixelSizeQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
83
|
+
self._xPixelSizeQTWI.setText(0, "x pixel size")
|
84
|
+
self._xPixelSizeMetricEntry = MetricEntry("", parent=self)
|
85
|
+
self._tree.setItemWidget(self._xPixelSizeQTWI, 1, self._xPixelSizeMetricEntry)
|
86
|
+
self._editableWidgets.append(self._xPixelSizeMetricEntry)
|
87
|
+
|
88
|
+
self._yPixelSizeQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
89
|
+
self._yPixelSizeQTWI.setText(0, "y pixel size")
|
90
|
+
self._yPixelSizeMetricEntry = MetricEntry("", parent=self)
|
91
|
+
self._tree.setItemWidget(self._yPixelSizeQTWI, 1, self._yPixelSizeMetricEntry)
|
92
|
+
self._editableWidgets.append(self._yPixelSizeMetricEntry)
|
93
|
+
|
94
|
+
## distance
|
95
|
+
self._sampleDetectorDistanceQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
96
|
+
self._sampleDetectorDistanceQTWI.setText(0, "distance")
|
97
|
+
self._distanceMetricEntry = MetricEntry("", parent=self)
|
98
|
+
self._tree.setItemWidget(
|
99
|
+
self._sampleDetectorDistanceQTWI, 1, self._distanceMetricEntry
|
100
|
+
)
|
101
|
+
self._editableWidgets.append(self._distanceMetricEntry)
|
102
|
+
## field of view
|
103
|
+
self._fieldOfViewQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
104
|
+
self._fieldOfViewQTWI.setText(0, "field of view")
|
105
|
+
self._fieldOfViewCB = qt.QComboBox(self)
|
106
|
+
for value in _FOV.values():
|
107
|
+
self._fieldOfViewCB.addItem(value)
|
108
|
+
self._tree.setItemWidget(self._fieldOfViewQTWI, 1, self._fieldOfViewCB)
|
109
|
+
self._editableWidgets.append(self._fieldOfViewCB)
|
110
|
+
## x flipped
|
111
|
+
self._xFlippedQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
112
|
+
self._xFlippedQTWI.setText(0, "x flipped")
|
113
|
+
self._xFlippedCB = qt.QCheckBox("", self)
|
114
|
+
self._tree.setItemWidget(self._xFlippedQTWI, 1, self._xFlippedCB)
|
115
|
+
self._editableWidgets.append(self._xFlippedCB)
|
116
|
+
|
117
|
+
## y flipped
|
118
|
+
self._yFlippedQTWI = qt.QTreeWidgetItem(self._detectorQTWI)
|
119
|
+
self._yFlippedQTWI.setText(0, "y flipped")
|
120
|
+
self._yFlippedCB = qt.QCheckBox("", self)
|
121
|
+
self._tree.setItemWidget(self._yFlippedQTWI, 1, self._yFlippedCB)
|
122
|
+
self._editableWidgets.append(self._yFlippedCB)
|
123
|
+
|
124
|
+
# 2: sample
|
125
|
+
self._sampleQTWI = qt.QTreeWidgetItem(self._tree)
|
126
|
+
self._sampleQTWI.setText(0, "sample")
|
127
|
+
## x translation
|
128
|
+
self._xTranslationQTWI = qt.QTreeWidgetItem(self._sampleQTWI)
|
129
|
+
self._xTranslationQTWI.setText(0, "x translation")
|
130
|
+
self._xTranslationQLE = _TranslationMetricEntry(name="", parent=self)
|
131
|
+
self._tree.setItemWidget(self._xTranslationQTWI, 1, self._xTranslationQLE)
|
132
|
+
self._editableWidgets.append(self._xTranslationQLE)
|
133
|
+
|
134
|
+
## z translation
|
135
|
+
self._zTranslationQTWI = qt.QTreeWidgetItem(self._sampleQTWI)
|
136
|
+
self._zTranslationQTWI.setText(0, "z translation")
|
137
|
+
self._zTranslationQLE = _TranslationMetricEntry(name="", parent=self)
|
138
|
+
self._tree.setItemWidget(self._zTranslationQTWI, 1, self._zTranslationQLE)
|
139
|
+
self._editableWidgets.append(self._zTranslationQLE)
|
140
|
+
|
141
|
+
# set up
|
142
|
+
self._instrumentQTWI.setExpanded(True)
|
143
|
+
self._sampleQTWI.setExpanded(True)
|
144
|
+
self._beamQTWI.setExpanded(True)
|
145
|
+
self._detectorQTWI.setExpanded(True)
|
146
|
+
|
147
|
+
def getEditableWidgets(self):
|
148
|
+
return self._editableWidgets
|
149
|
+
|
150
|
+
def setScan(self, scan):
|
151
|
+
if scan is None:
|
152
|
+
self._scan = scan
|
153
|
+
elif not isinstance(scan, HDF5TomoScan):
|
154
|
+
raise TypeError(
|
155
|
+
f"{scan} is expected to be an instance of {HDF5TomoScan}. Not {type(scan)}"
|
156
|
+
)
|
157
|
+
else:
|
158
|
+
self._scan = weakref.ref(scan)
|
159
|
+
self._scanInfoQLE.setScan(scan)
|
160
|
+
# scan will only be read and not kept
|
161
|
+
self.update_tree()
|
162
|
+
|
163
|
+
def getScan(self):
|
164
|
+
if self._scan is None or self._scan() is None:
|
165
|
+
return None
|
166
|
+
else:
|
167
|
+
return self._scan()
|
168
|
+
|
169
|
+
def update_tree(self):
|
170
|
+
if self.getScan() is not None:
|
171
|
+
for fct in (
|
172
|
+
self._updateInstrument,
|
173
|
+
self._updateSample,
|
174
|
+
):
|
175
|
+
try:
|
176
|
+
fct()
|
177
|
+
except Exception as e:
|
178
|
+
_logger.info(e)
|
179
|
+
self._tree.resizeColumnToContents(0)
|
180
|
+
|
181
|
+
def _updateInstrument(self):
|
182
|
+
scan = self.getScan()
|
183
|
+
if scan is None:
|
184
|
+
return
|
185
|
+
else:
|
186
|
+
self._updateEnergy(scan=scan)
|
187
|
+
self._updatePixelSize(scan=scan)
|
188
|
+
self._updateFlipped(scan=scan)
|
189
|
+
self._updateFieldOfView(scan=scan)
|
190
|
+
self._updateDistance(scan=scan)
|
191
|
+
|
192
|
+
def _updateSample(self):
|
193
|
+
scan = self.getScan()
|
194
|
+
if scan is None:
|
195
|
+
return
|
196
|
+
else:
|
197
|
+
self._updateTranslations(scan=scan)
|
198
|
+
|
199
|
+
def _updateTranslations(self, scan: HDF5TomoScan):
|
200
|
+
assert isinstance(scan, HDF5TomoScan)
|
201
|
+
|
202
|
+
# note: for now and in order to allow edition we expect to have at most a unique value. Will fail for helicoidal
|
203
|
+
def reduce(values):
|
204
|
+
if values is None:
|
205
|
+
return None
|
206
|
+
values = numpy.array(values)
|
207
|
+
values = numpy.unique(
|
208
|
+
values[scan.image_key_control == ImageKey.PROJECTION.value]
|
209
|
+
)
|
210
|
+
if values.size == 1:
|
211
|
+
return values[0]
|
212
|
+
elif values.size == 0:
|
213
|
+
return None
|
214
|
+
else:
|
215
|
+
return f"{values[0]} ... {values[-1]}"
|
216
|
+
|
217
|
+
x_translation = reduce(scan.x_translation)
|
218
|
+
z_translation = reduce(scan.z_translation)
|
219
|
+
self._xTranslationQLE.setValue(x_translation)
|
220
|
+
self._zTranslationQLE.setValue(z_translation)
|
221
|
+
|
222
|
+
def _updateFieldOfView(self, scan):
|
223
|
+
idx = self._fieldOfViewCB.findText(_FOV.from_value(scan.field_of_view).value)
|
224
|
+
if idx > 0:
|
225
|
+
self._fieldOfViewCB.setCurrentIndex(idx)
|
226
|
+
|
227
|
+
def _updateFlipped(self, scan):
|
228
|
+
if scan.x_flipped is not None:
|
229
|
+
self._xFlippedCB.setChecked(scan.x_flipped)
|
230
|
+
if scan.y_flipped is not None:
|
231
|
+
self._yFlippedCB.setChecked(scan.y_flipped)
|
232
|
+
|
233
|
+
def _updateDistance(self, scan):
|
234
|
+
self._distanceMetricEntry.setValue(scan.distance)
|
235
|
+
|
236
|
+
def _updateEnergy(self, scan):
|
237
|
+
assert isinstance(scan, HDF5TomoScan)
|
238
|
+
energy = scan.energy
|
239
|
+
self._energyQLE.setValue(energy)
|
240
|
+
|
241
|
+
def _updateScanRange(self, scan):
|
242
|
+
assert isinstance(scan, HDF5TomoScan)
|
243
|
+
scan_range = scan.scan_range
|
244
|
+
self._scanRangeQLE.setText(1, str(scan_range))
|
245
|
+
|
246
|
+
def _updatePixelSize(self, scan):
|
247
|
+
assert isinstance(scan, HDF5TomoScan)
|
248
|
+
x_pixel_size = scan.x_pixel_size
|
249
|
+
y_pixel_size = scan.y_pixel_size
|
250
|
+
self._xPixelSizeMetricEntry.setValue(x_pixel_size)
|
251
|
+
self._yPixelSizeMetricEntry.setValue(y_pixel_size)
|
252
|
+
|
253
|
+
def clear(self):
|
254
|
+
self._tree.clear()
|
255
|
+
|
256
|
+
def overwriteNXtomo(self):
|
257
|
+
"""overwrite data on disk"""
|
258
|
+
scan = self.getScan()
|
259
|
+
if scan is None:
|
260
|
+
_logger.warning("no scan found to be saved")
|
261
|
+
return
|
262
|
+
nexus_paths = get_nexus_paths(scan.nexus_version)
|
263
|
+
assert isinstance(scan, HDF5TomoScan)
|
264
|
+
with HDF5File(scan.master_file, mode="a") as h5f:
|
265
|
+
entry = h5f[scan.entry]
|
266
|
+
# overwrite energy
|
267
|
+
energy = self._energyQLE.getValue()
|
268
|
+
self.__write_to_file(
|
269
|
+
entry=entry,
|
270
|
+
path=nexus_paths.ENERGY_PATH,
|
271
|
+
value=energy,
|
272
|
+
name="energy",
|
273
|
+
expected_type=float,
|
274
|
+
units="kev",
|
275
|
+
)
|
276
|
+
# overwrite x pixel size
|
277
|
+
self.__write_to_file(
|
278
|
+
entry=entry,
|
279
|
+
path=nexus_paths.X_PIXEL_SIZE_PATH,
|
280
|
+
value=self._xPixelSizeMetricEntry.getValue(),
|
281
|
+
name="x pixel size",
|
282
|
+
expected_type=float,
|
283
|
+
units="m",
|
284
|
+
)
|
285
|
+
# overwrite y pixel size
|
286
|
+
self.__write_to_file(
|
287
|
+
entry=entry,
|
288
|
+
path=nexus_paths.Y_PIXEL_SIZE_PATH,
|
289
|
+
value=self._yPixelSizeMetricEntry.getValue(),
|
290
|
+
name="y pixel size",
|
291
|
+
expected_type=float,
|
292
|
+
units="m",
|
293
|
+
)
|
294
|
+
n_frames = len(scan.image_key_control)
|
295
|
+
|
296
|
+
# overwrite x translation
|
297
|
+
self.__write_to_file(
|
298
|
+
entry=entry,
|
299
|
+
path=nexus_paths.X_TRANS_PATH,
|
300
|
+
value=self._xTranslationQLE.getValue(),
|
301
|
+
name="x translation",
|
302
|
+
expected_type=float,
|
303
|
+
n_value=n_frames,
|
304
|
+
units="m",
|
305
|
+
)
|
306
|
+
# overwrite z translation
|
307
|
+
self.__write_to_file(
|
308
|
+
entry=entry,
|
309
|
+
path=nexus_paths.Z_TRANS_PATH,
|
310
|
+
value=self._zTranslationQLE.getValue(),
|
311
|
+
name="z translation",
|
312
|
+
expected_type=float,
|
313
|
+
n_value=n_frames,
|
314
|
+
units="m",
|
315
|
+
)
|
316
|
+
# overwrite sample detector distance
|
317
|
+
self.__write_to_file(
|
318
|
+
entry=entry,
|
319
|
+
path=nexus_paths.DISTANCE_PATH,
|
320
|
+
value=self._distanceMetricEntry.getValue(),
|
321
|
+
name="z translation",
|
322
|
+
expected_type=float,
|
323
|
+
units="m",
|
324
|
+
)
|
325
|
+
# overwrite FOV
|
326
|
+
self.__write_to_file(
|
327
|
+
entry=entry,
|
328
|
+
path=nexus_paths.FOV_PATH,
|
329
|
+
value=self._fieldOfViewCB.currentText(),
|
330
|
+
name="field of view",
|
331
|
+
expected_type=str,
|
332
|
+
)
|
333
|
+
# overwrite x flipped
|
334
|
+
self.__write_to_file(
|
335
|
+
entry=entry,
|
336
|
+
path="/".join(
|
337
|
+
(
|
338
|
+
nexus_paths.INSTRUMENT_PATH,
|
339
|
+
nexus_paths.nx_instrument_paths.DETECTOR_PATH,
|
340
|
+
nexus_paths.nx_detector_paths.X_FLIPPED,
|
341
|
+
)
|
342
|
+
),
|
343
|
+
value=self._xFlippedCB.isChecked(),
|
344
|
+
name="x flipped",
|
345
|
+
expected_type=bool,
|
346
|
+
)
|
347
|
+
# overwrite y flipped
|
348
|
+
self.__write_to_file(
|
349
|
+
entry=entry,
|
350
|
+
path="/".join(
|
351
|
+
(
|
352
|
+
nexus_paths.INSTRUMENT_PATH,
|
353
|
+
nexus_paths.nx_instrument_paths.DETECTOR_PATH,
|
354
|
+
nexus_paths.nx_detector_paths.Y_FLIPPED,
|
355
|
+
)
|
356
|
+
),
|
357
|
+
value=self._yFlippedCB.isChecked(),
|
358
|
+
name="y flipped",
|
359
|
+
expected_type=bool,
|
360
|
+
)
|
361
|
+
# clear caches to make sure all modifications will be considered
|
362
|
+
scan.clear_caches()
|
363
|
+
scan.clear_frames_caches()
|
364
|
+
|
365
|
+
@staticmethod
|
366
|
+
def _newValueIsExistingValue(dataset: h5py.Dataset, new_value, units):
|
367
|
+
"""
|
368
|
+
return true if the given value is same as the one stored
|
369
|
+
"""
|
370
|
+
current_value = h5py_read_dataset(dataset)
|
371
|
+
attrs = dataset.attrs
|
372
|
+
current_unit = attrs.get("units", attrs.get("unit", None))
|
373
|
+
if units != current_unit:
|
374
|
+
# if the unit is not the same, eithen if the value is the same we will overwrite it
|
375
|
+
return False
|
376
|
+
else:
|
377
|
+
if isinstance(new_value, numpy.ndarray) and isinstance(
|
378
|
+
current_value, numpy.ndarray
|
379
|
+
):
|
380
|
+
return numpy.array_equal(new_value, current_value)
|
381
|
+
elif numpy.isscalar(current_value) and numpy.isscalar(new_value):
|
382
|
+
return current_value == new_value
|
383
|
+
else:
|
384
|
+
return False
|
385
|
+
|
386
|
+
@staticmethod
|
387
|
+
def __write_to_file(entry, path, value, name, expected_type, n_value=1, units=None):
|
388
|
+
if path is None:
|
389
|
+
# if the path does not exists (no handled by this version of nexus for example)
|
390
|
+
return
|
391
|
+
|
392
|
+
# try to cast the value
|
393
|
+
if isinstance(value, str):
|
394
|
+
value = value.replace(" ", "")
|
395
|
+
if value.lower() == "none" or "..." in value:
|
396
|
+
# if value is not defined or is an array not overwrite by the user (case of the ... )
|
397
|
+
return
|
398
|
+
elif value is None:
|
399
|
+
pass
|
400
|
+
else:
|
401
|
+
try:
|
402
|
+
value = expected_type(value)
|
403
|
+
except (ValueError, TypeError) as e:
|
404
|
+
_logger.error(f"Fail to overwrite {name} of {entry.name}. Error is {e}")
|
405
|
+
return
|
406
|
+
|
407
|
+
if path in entry:
|
408
|
+
if NXtomoEditor._newValueIsExistingValue(
|
409
|
+
dataset=entry[path], new_value=value, units=units
|
410
|
+
):
|
411
|
+
# if no need to overwrite
|
412
|
+
return
|
413
|
+
else:
|
414
|
+
del entry[path]
|
415
|
+
if value is None:
|
416
|
+
return
|
417
|
+
elif n_value == 1:
|
418
|
+
entry[path] = value
|
419
|
+
else:
|
420
|
+
entry[path] = numpy.array([value] * n_value)
|
421
|
+
if units is not None:
|
422
|
+
entry[path].attrs["units"] = units
|
423
|
+
|
424
|
+
|
425
|
+
class _TranslationMetricEntry(MetricEntry):
|
426
|
+
LOADED_ARRAY = "loaded array"
|
427
|
+
|
428
|
+
class TranslationValidator(qt.QDoubleValidator):
|
429
|
+
def __init__(self, *args, **kwargs):
|
430
|
+
super().__init__(*args, **kwargs)
|
431
|
+
self.setNotation(qt.QDoubleValidator.ScientificNotation)
|
432
|
+
|
433
|
+
def validate(self, a0: str, a1: int):
|
434
|
+
if "..." in a0:
|
435
|
+
return (qt.QDoubleValidator.Acceptable, a0, a1)
|
436
|
+
else:
|
437
|
+
return super().validate(a0, a1)
|
438
|
+
|
439
|
+
def __init__(self, name, default_unit="m", parent=None):
|
440
|
+
super().__init__(name, default_unit, parent)
|
441
|
+
self._qlePixelSize.setValidator(self.TranslationValidator(self))
|
442
|
+
|
443
|
+
def getValue(self):
|
444
|
+
"""
|
445
|
+
|
446
|
+
:return: the value in meter
|
447
|
+
:rtype: float
|
448
|
+
"""
|
449
|
+
if "..." in self._qlePixelSize.text():
|
450
|
+
# in this case this is the representation of an array, we don;t wan't to overwrite it
|
451
|
+
return self.LOADED_ARRAY
|
452
|
+
if self._qlePixelSize.text() in ("unknown", ""):
|
453
|
+
return None
|
454
|
+
else:
|
455
|
+
return float(self._qlePixelSize.text()) * self.getCurrentUnit()
|
456
|
+
|
457
|
+
|
458
|
+
class EnergyEntry(qt.QLineEdit):
|
459
|
+
def __init__(self, *args, **kwargs):
|
460
|
+
super().__init__(*args, **kwargs)
|
461
|
+
self.setValidator(MetricEntry.DoubleValidator())
|
462
|
+
|
463
|
+
def setValue(self, a0):
|
464
|
+
if a0 is None:
|
465
|
+
a0 = "unknown"
|
466
|
+
else:
|
467
|
+
a0 = str(a0)
|
468
|
+
super().setText(a0)
|
469
|
+
|
470
|
+
def getValue(self) -> Optional[float]:
|
471
|
+
txt = self.text().replace(" ", "")
|
472
|
+
if txt in ("unknown", ""):
|
473
|
+
return None
|
474
|
+
else:
|
475
|
+
return float(txt)
|
@@ -28,17 +28,16 @@ __license__ = "MIT"
|
|
28
28
|
__date__ = "28/10/2020"
|
29
29
|
|
30
30
|
|
31
|
-
import unittest
|
32
31
|
import pytest
|
33
32
|
from silx.gui import qt
|
34
33
|
from silx.gui.utils.testutils import TestCaseQt
|
35
|
-
from tomwer.
|
34
|
+
from tomwer.tests.utils import skip_gui_test
|
36
35
|
from tomwer.core.utils.scanutils import MockHDF5
|
37
36
|
from tomwer.gui.edit.dkrfpatch import DarkRefPatchWidget
|
38
37
|
from tomwer.gui.edit.dkrfpatch import _DarkOrFlatUrl
|
39
38
|
from silx.io.url import DataUrl
|
40
39
|
from tomoscan.esrf.scan.utils import get_data
|
41
|
-
from tomoscan.esrf.hdf5scan import ImageKey
|
40
|
+
from tomoscan.esrf.scan.hdf5scan import ImageKey
|
42
41
|
from nxtomomill.utils import add_dark_flat_nx_file
|
43
42
|
from tomwer.core.scan.hdf5scan import HDF5TomoScan
|
44
43
|
import tempfile
|
@@ -210,14 +209,3 @@ class TestDarkRefPatchWidget(TestCaseQt):
|
|
210
209
|
darks_end=url_ed,
|
211
210
|
flats_end=url_ef,
|
212
211
|
)
|
213
|
-
|
214
|
-
|
215
|
-
def suite():
|
216
|
-
test_suite = unittest.TestSuite()
|
217
|
-
for ui in (TestDarkRefPatchWidget, TestDarkOrFlatUrl):
|
218
|
-
test_suite.addTest(unittest.defaultTestLoader.loadTestsFromTestCase(ui))
|
219
|
-
return test_suite
|
220
|
-
|
221
|
-
|
222
|
-
if __name__ == "__main__":
|
223
|
-
unittest.main(defaultTest="suite")
|
@@ -30,14 +30,14 @@ __date__ = "28/10/2020"
|
|
30
30
|
|
31
31
|
from silx.gui import qt
|
32
32
|
from silx.gui.utils.testutils import TestCaseQt, SignalListener
|
33
|
-
from tomwer.
|
33
|
+
from tomwer.tests.utils import skip_gui_test
|
34
34
|
from tomwer.gui.edit.imagekeyeditor import (
|
35
35
|
ImageKeyDialog,
|
36
36
|
ImageKeyUpgraderWidget,
|
37
37
|
_AddImageKeyUpgradeOperation,
|
38
38
|
)
|
39
39
|
from tomwer.core.utils.scanutils import MockHDF5
|
40
|
-
from tomoscan.esrf.hdf5scan import ImageKey
|
40
|
+
from tomoscan.esrf.scan.hdf5scan import ImageKey
|
41
41
|
import tempfile
|
42
42
|
import pytest
|
43
43
|
|
@@ -0,0 +1,155 @@
|
|
1
|
+
import os
|
2
|
+
import pytest
|
3
|
+
import numpy
|
4
|
+
from tomoscan.scanbase import _FOV
|
5
|
+
from tomoscan.esrf.scan.hdf5scan import ImageKey
|
6
|
+
from tomoscan.unitsystem.metricsystem import MetricSystem
|
7
|
+
from tomoscan.unitsystem.energysystem import EnergySI
|
8
|
+
from tomwer.core.scan.hdf5scan import HDF5TomoScan
|
9
|
+
from tomwer.gui.edit.nxtomoeditor import NXtomoEditor, _TranslationMetricEntry
|
10
|
+
from nxtomomill.nexus.nxtomo import NXtomo
|
11
|
+
from tomwer.tests.conftest import qtapp # noqa F401
|
12
|
+
from silx.gui import qt
|
13
|
+
|
14
|
+
|
15
|
+
@pytest.mark.parametrize("x_pixel_size", (None, 0.12))
|
16
|
+
@pytest.mark.parametrize("y_pixel_size", (None, 0.0065))
|
17
|
+
@pytest.mark.parametrize("field_of_view", _FOV.values())
|
18
|
+
@pytest.mark.parametrize("distance", (None, 1.2))
|
19
|
+
@pytest.mark.parametrize("energy", (None, 23.5))
|
20
|
+
@pytest.mark.parametrize("x_flipped", (True, False))
|
21
|
+
@pytest.mark.parametrize("y_flipped", (True, False))
|
22
|
+
@pytest.mark.parametrize("x_translation", (None, numpy.ones(12), numpy.arange(12)))
|
23
|
+
@pytest.mark.parametrize("z_translation", (None, numpy.zeros(12), numpy.arange(12, 24)))
|
24
|
+
def test_nx_editor(
|
25
|
+
tmp_path,
|
26
|
+
qtapp, # noqa F811
|
27
|
+
x_pixel_size,
|
28
|
+
y_pixel_size,
|
29
|
+
field_of_view,
|
30
|
+
distance,
|
31
|
+
energy,
|
32
|
+
x_flipped,
|
33
|
+
y_flipped,
|
34
|
+
x_translation,
|
35
|
+
z_translation,
|
36
|
+
):
|
37
|
+
# 1.0 create nx tomo with raw data
|
38
|
+
nx_tomo = NXtomo()
|
39
|
+
nx_tomo.instrument.detector.x_pixel_size = x_pixel_size
|
40
|
+
nx_tomo.instrument.detector.y_pixel_size = y_pixel_size
|
41
|
+
nx_tomo.instrument.detector.field_of_view = field_of_view
|
42
|
+
nx_tomo.instrument.detector.distance = distance
|
43
|
+
nx_tomo.instrument.detector.x_flipped = x_flipped
|
44
|
+
nx_tomo.instrument.detector.y_flipped = y_flipped
|
45
|
+
nx_tomo.energy = energy
|
46
|
+
nx_tomo.sample.x_translation = x_translation
|
47
|
+
nx_tomo.sample.z_translation = z_translation
|
48
|
+
nx_tomo.instrument.detector.image_key_control = [ImageKey.PROJECTION.value] * 12
|
49
|
+
nx_tomo.instrument.detector.data = numpy.empty(shape=(12, 10, 10))
|
50
|
+
nx_tomo.sample.rotation_angle = numpy.linspace(0, 20, num=12)
|
51
|
+
|
52
|
+
file_path = os.path.join(tmp_path, "nxtomo.nx")
|
53
|
+
entry = "entry0000"
|
54
|
+
nx_tomo.save(
|
55
|
+
file_path=file_path,
|
56
|
+
data_path=entry,
|
57
|
+
)
|
58
|
+
|
59
|
+
scan = HDF5TomoScan(file_path, entry)
|
60
|
+
|
61
|
+
# 2.0 create the widget and do the edition
|
62
|
+
widget = NXtomoEditor()
|
63
|
+
widget.setScan(scan=scan)
|
64
|
+
# widget.show()
|
65
|
+
|
66
|
+
# 3.0 check data have been corrcetly loaded
|
67
|
+
def check_metric(expected_value, current_value):
|
68
|
+
if expected_value is None:
|
69
|
+
return current_value is None
|
70
|
+
return numpy.isclose(expected_value, float(current_value))
|
71
|
+
|
72
|
+
assert check_metric(x_pixel_size, widget._xPixelSizeMetricEntry.getValue())
|
73
|
+
assert widget._xPixelSizeMetricEntry._qcbUnit.currentText() == "m"
|
74
|
+
assert check_metric(y_pixel_size, widget._yPixelSizeMetricEntry.getValue())
|
75
|
+
assert widget._yPixelSizeMetricEntry._qcbUnit.currentText() == "m"
|
76
|
+
|
77
|
+
assert check_metric(distance, widget._distanceMetricEntry.getValue())
|
78
|
+
assert widget._distanceMetricEntry._qcbUnit.currentText() == "m"
|
79
|
+
|
80
|
+
assert field_of_view == widget._fieldOfViewCB.currentText()
|
81
|
+
assert x_flipped == widget._xFlippedCB.isChecked()
|
82
|
+
assert y_flipped == widget._yFlippedCB.isChecked()
|
83
|
+
|
84
|
+
if energy is None:
|
85
|
+
assert widget._energyQLE.getValue() is None
|
86
|
+
else:
|
87
|
+
assert numpy.isclose(energy, widget._energyQLE.getValue())
|
88
|
+
|
89
|
+
def check_translation(expected_value, current_value):
|
90
|
+
if expected_value is None:
|
91
|
+
return current_value is None
|
92
|
+
else:
|
93
|
+
u_values = numpy.unique(expected_value)
|
94
|
+
if u_values.size == 1:
|
95
|
+
return float(current_value) == u_values[0]
|
96
|
+
else:
|
97
|
+
return current_value is _TranslationMetricEntry.LOADED_ARRAY
|
98
|
+
|
99
|
+
assert check_translation(x_translation, widget._xTranslationQLE.getValue())
|
100
|
+
assert widget._xTranslationQLE._qcbUnit.currentText() == "m"
|
101
|
+
assert check_translation(z_translation, widget._zTranslationQLE.getValue())
|
102
|
+
assert widget._zTranslationQLE._qcbUnit.currentText() == "m"
|
103
|
+
|
104
|
+
# 4.0 edit some parameters
|
105
|
+
widget._energyQLE.setText("23.789")
|
106
|
+
widget._xPixelSizeMetricEntry.setUnit("nm")
|
107
|
+
widget._yPixelSizeMetricEntry.setValue(2.1e-7)
|
108
|
+
widget._distanceMetricEntry.setValue("unknown")
|
109
|
+
widget._fieldOfViewCB.setCurrentText(_FOV.HALF.value)
|
110
|
+
widget._xFlippedCB.setChecked(not x_flipped)
|
111
|
+
widget._xTranslationQLE.setValue(1.8)
|
112
|
+
widget._xTranslationQLE.setUnit("mm")
|
113
|
+
widget._zTranslationQLE.setValue(2.8)
|
114
|
+
widget._zTranslationQLE.setUnit("m")
|
115
|
+
|
116
|
+
# 5.0 save
|
117
|
+
widget.overwriteNXtomo()
|
118
|
+
|
119
|
+
# 6.0 make sure data have been overwrite
|
120
|
+
overwrite_nx_tomo = NXtomo().load(
|
121
|
+
file_path=file_path,
|
122
|
+
data_path=entry,
|
123
|
+
)
|
124
|
+
|
125
|
+
assert overwrite_nx_tomo.energy.value == 23.789
|
126
|
+
assert overwrite_nx_tomo.energy.unit == EnergySI.KILOELECTRONVOLT
|
127
|
+
if x_pixel_size is None:
|
128
|
+
assert overwrite_nx_tomo.instrument.detector.x_pixel_size.value is None
|
129
|
+
else:
|
130
|
+
assert numpy.isclose(
|
131
|
+
overwrite_nx_tomo.instrument.detector.x_pixel_size.value,
|
132
|
+
x_pixel_size * MetricSystem.NANOMETER.value,
|
133
|
+
)
|
134
|
+
assert overwrite_nx_tomo.instrument.detector.y_pixel_size.value == 2.1e-7
|
135
|
+
|
136
|
+
assert overwrite_nx_tomo.instrument.detector.distance.value is None
|
137
|
+
assert overwrite_nx_tomo.instrument.detector.field_of_view is _FOV.HALF
|
138
|
+
|
139
|
+
assert overwrite_nx_tomo.instrument.detector.x_flipped is not x_flipped
|
140
|
+
assert overwrite_nx_tomo.instrument.detector.y_flipped is y_flipped
|
141
|
+
|
142
|
+
numpy.testing.assert_array_almost_equal(
|
143
|
+
overwrite_nx_tomo.sample.x_translation.value,
|
144
|
+
numpy.array([1.8 * MetricSystem.MILLIMETER.value] * 12),
|
145
|
+
)
|
146
|
+
assert overwrite_nx_tomo.sample.x_translation.unit is MetricSystem.METER
|
147
|
+
numpy.testing.assert_array_almost_equal(
|
148
|
+
overwrite_nx_tomo.sample.z_translation.value,
|
149
|
+
numpy.array([2.8 * MetricSystem.METER.value] * 12),
|
150
|
+
)
|
151
|
+
assert overwrite_nx_tomo.sample.z_translation.unit is MetricSystem.METER
|
152
|
+
# end
|
153
|
+
widget.setAttribute(qt.Qt.WA_DeleteOnClose)
|
154
|
+
widget.close()
|
155
|
+
widget = None
|