tomwer 1.3.4__py3-none-any.whl → 1.3.26__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/widgets/reconstruction/DarkRefAndCopyOW.py +1 -0
- orangecontrib/tomwer/widgets/reconstruction/NabuHelicalPrepareWeightsDoubleOW.py +184 -184
- tomwer/app/canvas_launcher/mainwindow.py +0 -1
- tomwer/app/zstitching.py +0 -1
- tomwer/core/cluster/cluster.py +0 -9
- tomwer/core/process/control/datalistener/datalistener.py +15 -12
- tomwer/core/process/control/datawatcher/edfdwprocess.py +0 -9
- tomwer/core/process/reconstruction/axis/axis.py +3 -3
- tomwer/core/process/reconstruction/axis/params.py +3 -3
- tomwer/core/process/reconstruction/darkref/darkrefscopy.py +37 -8
- tomwer/core/process/reconstruction/nabu/nabucommon.py +3 -4
- tomwer/core/process/reconstruction/nabu/nabuscores.py +1 -0
- tomwer/core/process/reconstruction/nabu/nabuslices.py +6 -52
- tomwer/core/process/reconstruction/nabu/nabuvolume.py +2 -5
- tomwer/core/process/reconstruction/nabu/utils.py +10 -2
- tomwer/core/process/reconstruction/saaxis/saaxis.py +2 -0
- tomwer/core/process/reconstruction/sadeltabeta/sadeltabeta.py +2 -0
- tomwer/core/process/task.py +4 -2
- tomwer/core/process/test/test_data_transfer.py +4 -3
- tomwer/core/scan/blissscan.py +3 -3
- tomwer/core/scan/nxtomoscan.py +2 -2
- tomwer/core/scan/scanbase.py +5 -6
- tomwer/core/utils/scanutils.py +5 -1
- tomwer/gui/cluster/slurm.py +1 -21
- tomwer/gui/cluster/test/test_cluster.py +0 -1
- tomwer/gui/control/datawatcher/datawatcher.py +1 -24
- tomwer/gui/control/reducedarkflatselector.py +2 -2
- tomwer/gui/control/selectorwidgetbase.py +3 -1
- tomwer/gui/edit/dkrfpatch.py +4 -4
- tomwer/gui/edit/nxtomoeditor.py +28 -20
- tomwer/gui/edit/nxtomowarmer.py +3 -2
- tomwer/gui/edit/test/test_nx_editor.py +58 -1
- tomwer/gui/imagefromfile.py +2 -2
- tomwer/gui/qfolderdialog.py +4 -0
- tomwer/gui/reconstruction/axis/axis.py +16 -13
- tomwer/gui/reconstruction/axis/radioaxis.py +3 -1
- tomwer/gui/reconstruction/darkref/darkrefcopywidget.py +11 -0
- tomwer/gui/reconstruction/nabu/nabuconfig/preprocessing.py +16 -14
- tomwer/gui/reconstruction/saaxis/saaxis.py +2 -2
- tomwer/gui/stitching/stitching.py +8 -3
- tomwer/gui/visualization/dataviewer.py +27 -15
- tomwer/gui/visualization/diffviewer/diffviewer.py +9 -8
- tomwer/gui/visualization/volumeviewer.py +10 -4
- tomwer/io/utils/h5pyutils.py +3 -7
- tomwer/io/utils/utils.py +3 -3
- tomwer/synctools/stacks/reconstruction/castvolume.py +20 -5
- tomwer/synctools/stacks/reconstruction/dkrefcopy.py +10 -0
- tomwer/tests/datasets.py +5 -1
- tomwer/utils.py +1 -4
- tomwer/version.py +1 -1
- tomwer-1.3.26-py3.11-nspkg.pth +1 -0
- {tomwer-1.3.4.dist-info → tomwer-1.3.26.dist-info}/METADATA +34 -48
- {tomwer-1.3.4.dist-info → tomwer-1.3.26.dist-info}/RECORD +58 -58
- {tomwer-1.3.4.dist-info → tomwer-1.3.26.dist-info}/WHEEL +1 -1
- tomwer-1.3.4-py3.11-nspkg.pth +0 -1
- {tomwer-1.3.4.dist-info → tomwer-1.3.26.dist-info}/LICENSE +0 -0
- {tomwer-1.3.4.dist-info → tomwer-1.3.26.dist-info}/entry_points.txt +0 -0
- {tomwer-1.3.4.dist-info → tomwer-1.3.26.dist-info}/namespace_packages.txt +0 -0
- {tomwer-1.3.4.dist-info → tomwer-1.3.26.dist-info}/top_level.txt +0 -0
@@ -8,11 +8,11 @@ from silx.gui import qt
|
|
8
8
|
from silx.gui.plot import Plot2D
|
9
9
|
from silx.io.dictdump import h5todict
|
10
10
|
from silx.io.url import DataUrl
|
11
|
+
from silx.io.utils import open as open_hdf5
|
11
12
|
from silx.gui.dialog.DataFileDialog import DataFileDialog
|
12
13
|
|
13
14
|
from tomoscan.esrf.scan.utils import cwd_context
|
14
15
|
from tomoscan.framereducer.target import REDUCER_TARGET
|
15
|
-
from tomoscan.io import HDF5File, get_swmr_mode
|
16
16
|
|
17
17
|
from tomwer.io.utils import get_default_directory
|
18
18
|
|
@@ -249,7 +249,7 @@ class ReduceDarkFlatSelectorTableWidget(qt.QWidget):
|
|
249
249
|
if not os.path.exists(file_path):
|
250
250
|
_logger.error(f"file doesn't exists ({file_path})")
|
251
251
|
|
252
|
-
with
|
252
|
+
with open_hdf5(file_path) as h5f:
|
253
253
|
entries = tuple(h5f.keys())
|
254
254
|
|
255
255
|
res = []
|
@@ -156,7 +156,9 @@ class _SelectorWidget(qt.QMainWindow):
|
|
156
156
|
|
157
157
|
def removeSelectedDatasets(self):
|
158
158
|
sItem = self.dataList.selectedItems()
|
159
|
-
selection = [
|
159
|
+
selection = [
|
160
|
+
_item.data(qt.Qt.UserRole).get_identifier().to_str() for _item in sItem
|
161
|
+
]
|
160
162
|
|
161
163
|
with block_signals(self):
|
162
164
|
# make sure sigUpdated is called only once.
|
tomwer/gui/edit/dkrfpatch.py
CHANGED
@@ -36,8 +36,8 @@ from silx.gui import qt
|
|
36
36
|
from silx.gui.dialog.DataFileDialog import DataFileDialog
|
37
37
|
from silx.io.url import DataUrl
|
38
38
|
from silx.io.utils import h5py_read_dataset
|
39
|
+
from silx.io.utils import open as open_hdf5
|
39
40
|
from nxtomo.nxobject.nxdetector import ImageKey
|
40
|
-
from tomoscan.io import HDF5File, get_swmr_mode
|
41
41
|
|
42
42
|
import tomwer.core.utils.nxtomoutils as nxtomo_utils
|
43
43
|
from tomwer.core.scan.nxtomoscan import NXtomoScan
|
@@ -83,7 +83,7 @@ class _DarkOrFlatUrl(qt.QWidget):
|
|
83
83
|
url = self._redirectDataPath(url, logger=_logger)
|
84
84
|
|
85
85
|
def dataset_invalid(url):
|
86
|
-
with
|
86
|
+
with open_hdf5(url.file_path()) as h5s:
|
87
87
|
if not isinstance(h5s[url.data_path()], h5py.Dataset):
|
88
88
|
return True
|
89
89
|
return False
|
@@ -113,7 +113,7 @@ class _DarkOrFlatUrl(qt.QWidget):
|
|
113
113
|
|
114
114
|
def _redirectDataPath(self, url, logger=None):
|
115
115
|
try:
|
116
|
-
with
|
116
|
+
with open_hdf5(url.file_path()) as h5s:
|
117
117
|
node = h5s[url.data_path()]
|
118
118
|
|
119
119
|
if NXtomoScan.entry_is_nx_tomo(node):
|
@@ -188,7 +188,7 @@ class _DarkOrFlatUrl(qt.QWidget):
|
|
188
188
|
def _getImageKey(self, url):
|
189
189
|
# if we are on a 'detector / data dataset' then we can try to reach
|
190
190
|
# image_key information
|
191
|
-
with
|
191
|
+
with open_hdf5(url.file_path()) as h5s:
|
192
192
|
dataset = h5s[url.data_path()]
|
193
193
|
grp_parent = dataset.parent
|
194
194
|
if grp_parent is not None and NXtomoScan.is_nxdetector(grp_parent):
|
tomwer/gui/edit/nxtomoeditor.py
CHANGED
@@ -574,28 +574,36 @@ class NXtomoEditor(qt.QWidget):
|
|
574
574
|
),
|
575
575
|
solve_empty_dependency=True,
|
576
576
|
)
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
577
|
+
if nexus_paths.nx_detector_paths.NX_TRANSFORMATIONS is not None:
|
578
|
+
# old NXtomo are not handling NX_TRANSFORMATIONS
|
579
|
+
detector_transformation_path = "/".join(
|
580
|
+
(
|
581
|
+
nexus_paths.INSTRUMENT_PATH,
|
582
|
+
nexus_paths.nx_instrument_paths.DETECTOR_PATH,
|
583
|
+
nexus_paths.nx_detector_paths.NX_TRANSFORMATIONS,
|
584
|
+
),
|
585
|
+
)
|
586
|
+
if detector_transformation_path in entry:
|
587
|
+
del entry[detector_transformation_path]
|
588
|
+
|
589
|
+
detector_transformation_path = "/".join(
|
590
|
+
(scan.entry, detector_transformation_path)
|
591
|
+
)
|
592
|
+
else:
|
593
|
+
_logger.debug(
|
594
|
+
"Old version of NXtomo found. No information about transformation will be saved"
|
595
|
+
)
|
596
|
+
detector_transformation_path = None
|
597
|
+
|
598
|
+
if detector_transformation_path is not None:
|
599
|
+
dicttonx(
|
600
|
+
nx_dict,
|
601
|
+
h5file=scan.master_file,
|
602
|
+
h5path=detector_transformation_path,
|
603
|
+
update_mode="replace",
|
604
|
+
mode="a",
|
589
605
|
)
|
590
606
|
|
591
|
-
dicttonx(
|
592
|
-
nx_dict,
|
593
|
-
h5file=scan.master_file,
|
594
|
-
h5path=detector_transformation_path,
|
595
|
-
update_mode="replace",
|
596
|
-
mode="a",
|
597
|
-
)
|
598
|
-
|
599
607
|
# clear caches to make sure all modifications will be considered
|
600
608
|
scan.clear_caches()
|
601
609
|
scan.clear_frames_caches()
|
tomwer/gui/edit/nxtomowarmer.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
from typing import Optional
|
2
2
|
import h5py
|
3
3
|
from silx.gui import qt
|
4
|
+
from silx.io.utils import open as open_hdf5
|
5
|
+
|
4
6
|
from tomwer.core.scan.nxtomoscan import NXtomoScan
|
5
7
|
from tomwer.gui import icons
|
6
|
-
from tomoscan.io import HDF5File, get_swmr_mode
|
7
8
|
|
8
9
|
|
9
10
|
class NXtomoProxyWarmer(qt.QWidget):
|
@@ -44,7 +45,7 @@ class NXtomoProxyWarmer(qt.QWidget):
|
|
44
45
|
if scan is None:
|
45
46
|
self._activateWarning(False)
|
46
47
|
elif isinstance(scan, NXtomoScan):
|
47
|
-
with
|
48
|
+
with open_hdf5(scan.master_file) as h5f:
|
48
49
|
entry = h5f.get(
|
49
50
|
name=scan.entry, getclass=True, getlink=True, default=None
|
50
51
|
)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import os
|
2
|
+
import h5py
|
2
3
|
|
3
4
|
import numpy
|
4
5
|
import pytest
|
@@ -252,7 +253,7 @@ def test_nx_editor_lock(
|
|
252
253
|
# 3.0 save the nxtomo
|
253
254
|
widget.overwriteNXtomo()
|
254
255
|
|
255
|
-
# 4.0 check save went
|
256
|
+
# 4.0 check save went well
|
256
257
|
overwrite_nx_tomo = NXtomo().load(
|
257
258
|
file_path=file_path,
|
258
259
|
data_path=entry,
|
@@ -305,3 +306,59 @@ def test_nx_editor_lock(
|
|
305
306
|
"instrument.detector.x_flipped": (False, False),
|
306
307
|
"instrument.detector.y_flipped": (True, False),
|
307
308
|
}
|
309
|
+
|
310
|
+
|
311
|
+
def test_nxtomo_editor_with_missing_paths(
|
312
|
+
tmp_path,
|
313
|
+
qtapp, # noqa F811
|
314
|
+
):
|
315
|
+
"""
|
316
|
+
test widget behavior in the case some nxtomo path don't exist
|
317
|
+
"""
|
318
|
+
|
319
|
+
# create nx tomo with raw data
|
320
|
+
nx_tomo = NXtomo()
|
321
|
+
nx_tomo.instrument.detector.image_key_control = [ImageKey.PROJECTION.value] * 12
|
322
|
+
nx_tomo.instrument.detector.data = numpy.empty(shape=(12, 10, 10))
|
323
|
+
nx_tomo.sample.rotation_angle = numpy.linspace(0, 20, num=12)
|
324
|
+
|
325
|
+
file_path = os.path.join(tmp_path, "nxtomo.nx")
|
326
|
+
entry = "entry0000"
|
327
|
+
nx_tomo.save(
|
328
|
+
file_path=file_path,
|
329
|
+
data_path=entry,
|
330
|
+
)
|
331
|
+
# delete some path that can be missing in some case
|
332
|
+
with h5py.File(file_path, mode="a") as h5f:
|
333
|
+
assert "entry0000" in h5f
|
334
|
+
assert "entry0000/beam" not in h5f
|
335
|
+
assert "entry0000/instrument/beam" not in h5f
|
336
|
+
assert "entry0000/instrument/detector/distance" not in h5f
|
337
|
+
assert "entry0000/instrument/detector/x_pixel_size" not in h5f
|
338
|
+
assert "entry0000/instrument/detector/y_pixel_size" not in h5f
|
339
|
+
assert "entry0000/instrument/detector/transformations" not in h5f
|
340
|
+
|
341
|
+
scan = NXtomoScan(file_path, entry)
|
342
|
+
|
343
|
+
# create the widget and do the edition
|
344
|
+
widget = NXtomoEditor()
|
345
|
+
|
346
|
+
widget.setScan(scan=scan)
|
347
|
+
|
348
|
+
widget._distanceMetricEntry.setValue(0.05)
|
349
|
+
widget._energyEntry.setValue(50)
|
350
|
+
widget._xPixelSizeMetricEntry.setValue(0.02)
|
351
|
+
widget._yPixelSizeMetricEntry.setValue(0.03)
|
352
|
+
|
353
|
+
# overwrite the NXtomo
|
354
|
+
widget.overwriteNXtomo()
|
355
|
+
|
356
|
+
# check save went well
|
357
|
+
overwrite_nx_tomo = NXtomo().load(
|
358
|
+
file_path=file_path,
|
359
|
+
data_path=entry,
|
360
|
+
)
|
361
|
+
assert overwrite_nx_tomo.instrument.detector.x_pixel_size.value == 0.02
|
362
|
+
assert overwrite_nx_tomo.instrument.detector.y_pixel_size.value == 0.03
|
363
|
+
assert overwrite_nx_tomo.energy.value == 50
|
364
|
+
assert overwrite_nx_tomo.instrument.detector.distance.value == 0.05
|
tomwer/gui/imagefromfile.py
CHANGED
@@ -34,7 +34,7 @@ import logging
|
|
34
34
|
|
35
35
|
from silx.gui import qt
|
36
36
|
from silx.io.url import DataUrl
|
37
|
-
from
|
37
|
+
from silx.io.utils import open as open_hdf5
|
38
38
|
|
39
39
|
from tomwer.core.scan.scanbase import TomwerScanBase
|
40
40
|
from tomwer.synctools.imageloaderthread import ImageLoaderThread
|
@@ -71,7 +71,7 @@ class ImageFromFile(_Image):
|
|
71
71
|
|
72
72
|
def get_nabu_entry():
|
73
73
|
try:
|
74
|
-
with
|
74
|
+
with open_hdf5(_file) as h5s:
|
75
75
|
for node in h5s:
|
76
76
|
if "reconstruction" in h5s[node]:
|
77
77
|
return "/".join(
|
tomwer/gui/qfolderdialog.py
CHANGED
@@ -544,6 +544,10 @@ class QVolumeDialog(qt.QDialog):
|
|
544
544
|
constructor = TIFFVolume
|
545
545
|
elif file_extension in self._JP2K_EXTENSIONS:
|
546
546
|
constructor = JP2KVolume
|
547
|
+
else:
|
548
|
+
raise NotImplementedError(
|
549
|
+
f"unhandled file extension ({file_extension})"
|
550
|
+
)
|
547
551
|
volume = constructor(
|
548
552
|
folder=file_path,
|
549
553
|
volume_basename=None if basename in ("", None) else basename,
|
@@ -31,6 +31,7 @@ __license__ = "MIT"
|
|
31
31
|
__date__ = "14/10/2019"
|
32
32
|
|
33
33
|
|
34
|
+
import numpy
|
34
35
|
import logging
|
35
36
|
from typing import Optional
|
36
37
|
|
@@ -272,6 +273,7 @@ class _AxisWidget(qt.QMainWindow):
|
|
272
273
|
self._axis_params.relative_cor_value,
|
273
274
|
self._axis_params.absolute_cor_value,
|
274
275
|
)
|
276
|
+
self._controlWidget._positionInfo.setAxis(self._axis_params)
|
275
277
|
|
276
278
|
# connect signal / slots
|
277
279
|
self._controlWidget.sigComputationRequest.connect(self.sigComputationRequested)
|
@@ -663,7 +665,13 @@ class _PositionInfoWidget(qt.QWidget):
|
|
663
665
|
if self._relativePositionQLE.text().startswith((".", "?")):
|
664
666
|
return
|
665
667
|
else:
|
666
|
-
|
668
|
+
value = float(self._relativePositionQLE.text())
|
669
|
+
# make sure we only emit the signal if the value changed (and when the Qline has been edited).
|
670
|
+
if self._axis.relative_cor_value is None or (
|
671
|
+
self._axis is not None
|
672
|
+
and not numpy.isclose(value, self._axis.relative_cor_value, atol=1e-3)
|
673
|
+
):
|
674
|
+
self.sigRelativeValueSet.emit(value)
|
667
675
|
|
668
676
|
def _userUpdatedAbsolutePosition(self, *args, **kwargs):
|
669
677
|
palette = self.palette()
|
@@ -675,24 +683,19 @@ class _PositionInfoWidget(qt.QWidget):
|
|
675
683
|
if self._absolutePositionQLE.text().startswith((".", "?")):
|
676
684
|
return
|
677
685
|
else:
|
678
|
-
|
686
|
+
value = float(self._absolutePositionQLE.text())
|
687
|
+
# make sure we only emit the signal if the value changed (and when the Qline has been edited).
|
688
|
+
if self._axis.absolute_cor_value is None or (
|
689
|
+
self._axis is not None
|
690
|
+
and not numpy.isclose(value, self._axis.absolute_cor_value, atol=1e-3)
|
691
|
+
):
|
692
|
+
self.sigAbsolueValueSet.emit(value)
|
679
693
|
|
680
694
|
def setAxis(self, axis):
|
681
695
|
assert isinstance(axis, QAxisRP)
|
682
696
|
if axis == self._axis:
|
683
697
|
return
|
684
|
-
if self._axis is not None:
|
685
|
-
self._axis.sigChanged.disconnect(self._updatePosition)
|
686
698
|
self._axis = axis
|
687
|
-
self._axis.sigChanged.connect(self._updatePosition)
|
688
|
-
self._updatePosition()
|
689
|
-
|
690
|
-
def _updatePosition(self):
|
691
|
-
if self._axis:
|
692
|
-
self.setPosition(
|
693
|
-
relative_cor=self._axis.relative_cor_value,
|
694
|
-
abs_cor=self._axis.absolute_cor_value,
|
695
|
-
)
|
696
699
|
|
697
700
|
def getPosition(self):
|
698
701
|
return float(self._relativePositionQLE.text())
|
@@ -1148,7 +1148,6 @@ class _ShiftInformation(qt.QWidget):
|
|
1148
1148
|
self.setPalette(palette)
|
1149
1149
|
|
1150
1150
|
def _userEndEditing(self, *args, **kwargs):
|
1151
|
-
print("user end editing")
|
1152
1151
|
palette = self.palette()
|
1153
1152
|
palette.setColor(
|
1154
1153
|
self.backgroundRole(),
|
@@ -1647,6 +1646,9 @@ class _CalculationWidget(qt.QWidget):
|
|
1647
1646
|
def setEstimatedCorValue(self, value):
|
1648
1647
|
if value is not None:
|
1649
1648
|
self._qleNearPosQLE.setText(str(value))
|
1649
|
+
# note: keep self._axis_params up to date.
|
1650
|
+
if self._axis_params:
|
1651
|
+
self._axis_params.estimated_cor = value
|
1650
1652
|
|
1651
1653
|
def getEstimatedCor(self):
|
1652
1654
|
try:
|
@@ -54,6 +54,7 @@ class DarkRefAndCopyWidget(DarkRefWidget):
|
|
54
54
|
"""Signal emitted when the mode auto change"""
|
55
55
|
sigCopyActivationChanged = qt.Signal()
|
56
56
|
"""Signal emitted when the copy is activated or deactivated"""
|
57
|
+
sigClearCache = qt.Signal()
|
57
58
|
|
58
59
|
def __init__(self, save_dir: str, parent=None, reconsparams=None, process_id=None):
|
59
60
|
DarkRefWidget.__init__(
|
@@ -79,6 +80,7 @@ class DarkRefAndCopyWidget(DarkRefWidget):
|
|
79
80
|
self._refCopyWidget.sigCopyActivationChanged.connect(
|
80
81
|
self._triggerCopyActivation
|
81
82
|
)
|
83
|
+
self._refCopyWidget.sigClearCache.connect(self.sigClearCache)
|
82
84
|
|
83
85
|
def setRefSetBy(self, scan_id: str):
|
84
86
|
self._refCopyWidget._statusBar.showMessage(f"ref set from {scan_id}")
|
@@ -155,6 +157,8 @@ class RefCopyWidget(qt.QGroupBox):
|
|
155
157
|
"""Signal emitted when the mode auto change"""
|
156
158
|
sigCopyActivationChanged = qt.Signal()
|
157
159
|
"""Signal emitted when the copy is activated or deactivated"""
|
160
|
+
sigClearCache = qt.Signal()
|
161
|
+
"""Signal when the cache needs to be cleared"""
|
158
162
|
|
159
163
|
_DEFAULT_DIRECTORY = "/lbsram/data/visitor"
|
160
164
|
"""Default directory used when the user need to set path to references"""
|
@@ -190,6 +194,12 @@ class RefCopyWidget(qt.QGroupBox):
|
|
190
194
|
spacer = qt.QWidget(self)
|
191
195
|
spacer.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding)
|
192
196
|
self.layout().addWidget(spacer)
|
197
|
+
self._removeCacheFile = qt.QPushButton("clear cache")
|
198
|
+
self._removeCacheFile.setToolTip(
|
199
|
+
"Remove the file used for cacheing reduce dark / flat."
|
200
|
+
)
|
201
|
+
self.layout().addWidget(self._removeCacheFile)
|
202
|
+
|
193
203
|
self.layout().addWidget(self.__createStatusBarGUI())
|
194
204
|
|
195
205
|
self.setModeAuto(True)
|
@@ -201,6 +211,7 @@ class RefCopyWidget(qt.QGroupBox):
|
|
201
211
|
# connect signal / slot
|
202
212
|
self.toggled.connect(self._triggerCopyActivated)
|
203
213
|
self._qcbAutoMode.toggled.connect(self._triggerModeAutoChanged)
|
214
|
+
self._removeCacheFile.released.connect(self.sigClearCache)
|
204
215
|
|
205
216
|
def _triggerCopyActivated(self, *args, **kwargs):
|
206
217
|
self.sigCopyActivationChanged.emit()
|
@@ -40,6 +40,7 @@ from tomwer.core.process.reconstruction.nabu.utils import (
|
|
40
40
|
from tomwer.gui.reconstruction.nabu.nabuconfig.base import _NabuStageConfigBase
|
41
41
|
from tomwer.gui.utils.scrollarea import QComboBoxIgnoreWheel as QComboBox
|
42
42
|
from tomwer.gui.utils.scrollarea import QDoubleSpinBoxIgnoreWheel as QDoubleSpinBox
|
43
|
+
from tomwer.gui.utils.scrollarea import QSpinBoxIgnoreWheel as QSpinBox
|
43
44
|
from tomwer.utils import docstring
|
44
45
|
|
45
46
|
_logger = logging.getLogger(__name__)
|
@@ -104,7 +105,7 @@ class _NabuPreProcessingConfig(_NabuStageConfigBase, qt.QWidget):
|
|
104
105
|
self.layout().addWidget(self._sinoRingCorrectionMthd, 2, 2, 1, 1)
|
105
106
|
self.registerWidget(self._sinoRingCorrectionMthd, "required")
|
106
107
|
|
107
|
-
self._sinoRingsOpts = SinoRingsOptions(parent=self)
|
108
|
+
self._sinoRingsOpts = SinoRingsOptions(parent=self, scrollArea=scrollArea)
|
108
109
|
self.layout().addWidget(self._sinoRingsOpts, 3, 1, 1, 3)
|
109
110
|
|
110
111
|
## ccd filter
|
@@ -187,13 +188,14 @@ class _NabuPreProcessingConfig(_NabuStageConfigBase, qt.QWidget):
|
|
187
188
|
|
188
189
|
# option dedicated to Helical
|
189
190
|
## process file
|
190
|
-
self.
|
191
|
+
self._processFileLabel = qt.QLabel("file containing weights maps", self)
|
192
|
+
self.registerWidget(self._processFileLabel, "advanced")
|
193
|
+
self.layout().addWidget(self._processFileLabel, 20, 0, 1, 1)
|
194
|
+
self._processFileQLE = qt.QLineEdit("", self)
|
195
|
+
self.registerWidget(self._processFileQLE, "advanced")
|
191
196
|
self._processFileQLE.setToolTip(
|
192
197
|
"also know as 'process_file'. If you don't have this file it can be created from the 'helical-prepare-weights' widget"
|
193
198
|
)
|
194
|
-
self.layout().addWidget(self._processFileQLE, 20, 0, 1, 1)
|
195
|
-
self._processFileQLE = qt.QLineEdit("", self)
|
196
|
-
self.registerWidget(self._processFileQLE, "advanced")
|
197
199
|
self.layout().addWidget(self._processFileQLE, 20, 1, 1, 3)
|
198
200
|
|
199
201
|
# style
|
@@ -443,7 +445,7 @@ class _NabuPreProcessingConfig(_NabuStageConfigBase, qt.QWidget):
|
|
443
445
|
class SinoRingsOptions(qt.QWidget):
|
444
446
|
_VO_DIMS = ("horizontaly", "horizontaly and vertically")
|
445
447
|
|
446
|
-
def __init__(self, parent=None, *args, **kwargs):
|
448
|
+
def __init__(self, parent=None, scrollArea=None, *args, **kwargs):
|
447
449
|
super().__init__(parent, *args, **kwargs)
|
448
450
|
self._method = None
|
449
451
|
self.setLayout(qt.QFormLayout())
|
@@ -451,12 +453,12 @@ class SinoRingsOptions(qt.QWidget):
|
|
451
453
|
self.layout().setSpacing(0)
|
452
454
|
# munch parameters
|
453
455
|
self._sigmaMunchLabel = qt.QLabel("sigma", self)
|
454
|
-
self._sigmaMunch =
|
456
|
+
self._sigmaMunch = QDoubleSpinBox(self, scrollArea=scrollArea)
|
455
457
|
self._sigmaMunch.setRange(0.0, 2147483647)
|
456
458
|
self.layout().addRow(self._sigmaMunchLabel, self._sigmaMunch)
|
457
459
|
|
458
460
|
self._levelsMunchLabel = qt.QLabel("levels", self)
|
459
|
-
self._levelsMunch =
|
461
|
+
self._levelsMunch = QSpinBox(self, scrollArea=scrollArea)
|
460
462
|
self._levelsMunch.setRange(0, 2147483647)
|
461
463
|
self.layout().addRow(self._levelsMunchLabel, self._levelsMunch)
|
462
464
|
|
@@ -465,7 +467,7 @@ class SinoRingsOptions(qt.QWidget):
|
|
465
467
|
|
466
468
|
# vo parameters
|
467
469
|
self._snrVOLabel = qt.QLabel("snr", self)
|
468
|
-
self._snrVO =
|
470
|
+
self._snrVO = QDoubleSpinBox(self, scrollArea=scrollArea)
|
469
471
|
self._snrVO.setMinimum(0.0)
|
470
472
|
tooltip = "Ratio used to locate large stripes. Greater is less sensitive."
|
471
473
|
self._snrVO.setToolTip(tooltip)
|
@@ -473,7 +475,7 @@ class SinoRingsOptions(qt.QWidget):
|
|
473
475
|
self.layout().addRow(self._snrVOLabel, self._snrVO)
|
474
476
|
|
475
477
|
self._laSizeVOLabel = qt.QLabel("la_size", self)
|
476
|
-
self._laSizeVO =
|
478
|
+
self._laSizeVO = QSpinBox(self, scrollArea=scrollArea)
|
477
479
|
self._laSizeVO.setMinimum(0)
|
478
480
|
tooltip = "Window size of the median filter to remove large stripes."
|
479
481
|
self._laSizeVO.setToolTip(tooltip)
|
@@ -481,7 +483,7 @@ class SinoRingsOptions(qt.QWidget):
|
|
481
483
|
self.layout().addRow(self._laSizeVOLabel, self._laSizeVO)
|
482
484
|
|
483
485
|
self._smSizeVOLabel = qt.QLabel("sm_size", self)
|
484
|
-
self._smSizeVO =
|
486
|
+
self._smSizeVO = QSpinBox(self, scrollArea=scrollArea)
|
485
487
|
self._smSizeVO.setMinimum(0)
|
486
488
|
tooltip = "Window size of the median filter to remove small-to-medium stripes."
|
487
489
|
self._laSizeVO.setToolTip(tooltip)
|
@@ -489,18 +491,18 @@ class SinoRingsOptions(qt.QWidget):
|
|
489
491
|
self.layout().addRow(self._smSizeVOLabel, self._smSizeVO)
|
490
492
|
|
491
493
|
self._dimVOLabel = qt.QLabel("dimension", self)
|
492
|
-
self._dimVO =
|
494
|
+
self._dimVO = QComboBox(self, scrollArea=scrollArea)
|
493
495
|
self._dimVO.addItems(self._VO_DIMS)
|
494
496
|
self.layout().addRow(self._dimVOLabel, self._dimVO)
|
495
497
|
|
496
498
|
# sino mean deringer
|
497
499
|
self._sigmaLowLabel = qt.QLabel("signal low", self)
|
498
|
-
self._sigmaLow =
|
500
|
+
self._sigmaLow = QDoubleSpinBox(self, scrollArea=scrollArea)
|
499
501
|
self._sigmaLow.setMinimum(0.0)
|
500
502
|
self._sigmaHighLabel = qt.QLabel("signal high", self)
|
501
503
|
self.layout().addRow(self._sigmaLowLabel, self._sigmaLow)
|
502
504
|
|
503
|
-
self._sigmaHigh =
|
505
|
+
self._sigmaHigh = QDoubleSpinBox(self, scrollArea=scrollArea)
|
504
506
|
self._sigmaHigh.setMinimum(0.0)
|
505
507
|
tooltip = (
|
506
508
|
"sigma low and sigma high values are defining the standard deviation of "
|
@@ -66,14 +66,14 @@ class ScorePlot(_ScorePlot, constructor=CorSelection):
|
|
66
66
|
|
67
67
|
def _updateScores(self):
|
68
68
|
scan = self.__scan() if self.__scan else None
|
69
|
+
img_width = None
|
69
70
|
if scan is not None:
|
70
71
|
if scan.saaxis_params:
|
71
72
|
scan.saaxis_params.score_method = self.getScoreMethod()
|
72
73
|
img_width = scan.dim_1
|
73
74
|
# update autofocus
|
74
75
|
SAAxisTask.autofocus(scan)
|
75
|
-
|
76
|
-
img_width = None
|
76
|
+
|
77
77
|
self.setVarScores(
|
78
78
|
scores=self._scores,
|
79
79
|
score_method=self.getScoreMethod(),
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
|
+
import shutil
|
3
4
|
import tempfile
|
4
5
|
import functools
|
5
6
|
|
@@ -399,7 +400,7 @@ class ZStitchingWindow(qt.QMainWindow):
|
|
399
400
|
# separator
|
400
401
|
toolbar.addSeparator()
|
401
402
|
|
402
|
-
#
|
403
|
+
# configuration level / mode
|
403
404
|
self.__configurationModesAction = qt.QAction(self)
|
404
405
|
self.__configurationModesAction.setCheckable(False)
|
405
406
|
menu = qt.QMenu(self)
|
@@ -602,6 +603,8 @@ class ZStitchingWindow(qt.QMainWindow):
|
|
602
603
|
self._callbackToSetSlurmConfig = callback
|
603
604
|
|
604
605
|
def close(self):
|
606
|
+
# remove folder used for preview
|
607
|
+
shutil.rmtree(self._previewFolder, ignore_errors=True)
|
605
608
|
self._widget.close()
|
606
609
|
# requested for the waiting plot update
|
607
610
|
super().close()
|
@@ -618,7 +621,6 @@ class ZStitchingWindow(qt.QMainWindow):
|
|
618
621
|
|
619
622
|
def getPreviewFolder(self):
|
620
623
|
if self._previewFolder is None:
|
621
|
-
# TODO: improve management of file: must be removed when the widget is closed...
|
622
624
|
self._previewFolder = tempfile.mkdtemp(prefix="tomwer_stitcher_preview")
|
623
625
|
return self._previewFolder
|
624
626
|
|
@@ -782,7 +784,6 @@ class ZStitchingWindow(qt.QMainWindow):
|
|
782
784
|
tomo_obj = existing_tomo_obj.get(tomo_obj_id, None)
|
783
785
|
if tomo_obj is None:
|
784
786
|
continue
|
785
|
-
# TODO: recuperer le tomo obj a partir de l'identifiant
|
786
787
|
if update_requested[0]:
|
787
788
|
tomo_obj.stitching_metadata.setPxPos(int(new_axis_0_pos), 0)
|
788
789
|
if update_requested[2]:
|
@@ -859,6 +860,10 @@ class ZStitchingWindow(qt.QMainWindow):
|
|
859
860
|
level >= ConfigurationLevel.ADVANCED
|
860
861
|
)
|
861
862
|
|
863
|
+
def close(self):
|
864
|
+
shutil.rmtree(self._previewFolder, ignore_errors=True)
|
865
|
+
super().close()
|
866
|
+
|
862
867
|
|
863
868
|
def concatenate_dict(dict_1, dict_2) -> dict:
|
864
869
|
"""update dict which has dict as values. And we want concatenate those values to"""
|
@@ -34,7 +34,6 @@ from silx.gui import qt
|
|
34
34
|
from silx.gui.dialog.ColormapDialog import DisplayMode
|
35
35
|
from silx.gui.plot.ImageStack import ImageStack as _ImageStack
|
36
36
|
from silx.gui.plot.ImageStack import UrlLoader
|
37
|
-
from silx.gui.utils.signal import SignalProxy
|
38
37
|
from silx.io.url import DataUrl
|
39
38
|
from silx.utils.enum import Enum as _Enum
|
40
39
|
|
@@ -86,17 +85,6 @@ class DataViewer(qt.QMainWindow):
|
|
86
85
|
self._viewer.getPlotWidget().setKeepDataAspectRatio(True)
|
87
86
|
self.setCentralWidget(self._viewer)
|
88
87
|
|
89
|
-
# signal / slot
|
90
|
-
# add a signal proxy on the QSlider
|
91
|
-
self._viewer._slider.sigCurrentUrlIndexChanged.disconnect(
|
92
|
-
self._viewer.setCurrentUrlIndex
|
93
|
-
)
|
94
|
-
self._proxySig = SignalProxy(
|
95
|
-
self._viewer._slider.sigCurrentUrlIndexChanged,
|
96
|
-
delay=0.3,
|
97
|
-
slot=self._urlIndexDelayed,
|
98
|
-
)
|
99
|
-
|
100
88
|
# display control
|
101
89
|
self._controls = DisplayControl(parent=self)
|
102
90
|
self._controlsDW = qt.QDockWidget(self)
|
@@ -110,6 +98,33 @@ class DataViewer(qt.QMainWindow):
|
|
110
98
|
self._controls.sigDisplayModeChanged.connect(self._updateDisplay)
|
111
99
|
self._controls.sigDisplayModeChanged.connect(self.sigConfigChanged)
|
112
100
|
|
101
|
+
# upgrade of the slider (see details in '__sliderPressed' docstring).
|
102
|
+
horizontal_slider = self._viewer._slider
|
103
|
+
horizontal_slider._slider.sliderReleased.connect(self.__sliderReleased)
|
104
|
+
horizontal_slider._slider.sliderPressed.connect(self.__sliderPressed)
|
105
|
+
|
106
|
+
def __sliderPressed(self):
|
107
|
+
"""
|
108
|
+
today each time the slider value is modified it will load the frame and display it
|
109
|
+
but in our case we cannot afford it as it will take too much memory.
|
110
|
+
So we will disconnect the callback (self._viewer.setCurrentUrlIndex) until the slider is active
|
111
|
+
and reactivate it afterwards
|
112
|
+
"""
|
113
|
+
self._viewer._slider.sigCurrentUrlIndexChanged.disconnect(
|
114
|
+
self._viewer.setCurrentUrlIndex
|
115
|
+
)
|
116
|
+
|
117
|
+
def __sliderReleased(self):
|
118
|
+
"""
|
119
|
+
See details in '__sliderPressed'
|
120
|
+
"""
|
121
|
+
horizontal_slider = self._viewer._slider
|
122
|
+
self._viewer._slider.sigCurrentUrlIndexChanged.connect(
|
123
|
+
self._viewer.setCurrentUrlIndex
|
124
|
+
)
|
125
|
+
# set the url with the latest value to display the requested frame
|
126
|
+
self._viewer.setCurrentUrlIndex(horizontal_slider.value())
|
127
|
+
|
113
128
|
def getPlotWidget(self):
|
114
129
|
return self._viewer.getPlotWidget()
|
115
130
|
|
@@ -134,9 +149,6 @@ class DataViewer(qt.QMainWindow):
|
|
134
149
|
self._viewer = None
|
135
150
|
super().close()
|
136
151
|
|
137
|
-
def _urlIndexDelayed(self, *args, **kwargs):
|
138
|
-
self._viewer.setCurrentUrlIndex(args[0][0])
|
139
|
-
|
140
152
|
def getScan(self):
|
141
153
|
if self._scan:
|
142
154
|
return self._scan()
|
@@ -25,6 +25,7 @@
|
|
25
25
|
"""
|
26
26
|
contains gui relative frame difference display
|
27
27
|
"""
|
28
|
+
from __future__ import annotations
|
28
29
|
|
29
30
|
__authors__ = ["H. Payno"]
|
30
31
|
__license__ = "MIT"
|
@@ -148,7 +149,7 @@ class _FrameSelector(qt.QWidget):
|
|
148
149
|
return
|
149
150
|
|
150
151
|
urls = []
|
151
|
-
|
152
|
+
urls_to_angles: dict[str, float] = {}
|
152
153
|
self._currentFrameUrlsText.clear()
|
153
154
|
self._frameUrlCB.clear()
|
154
155
|
if type_selected == self.FrameType.DARKS:
|
@@ -162,7 +163,7 @@ class _FrameSelector(qt.QWidget):
|
|
162
163
|
angles_and_urls = self._scan.get_proj_angle_url(with_alignment=False)
|
163
164
|
for angle, url in angles_and_urls.items():
|
164
165
|
urls.append(url)
|
165
|
-
|
166
|
+
urls_to_angles[url.path()] = angle
|
166
167
|
else:
|
167
168
|
urls = self._scan.projections.values()
|
168
169
|
elif type_selected == self.FrameType.ALIGN_PROJ:
|
@@ -174,14 +175,14 @@ class _FrameSelector(qt.QWidget):
|
|
174
175
|
raise ValueError(f"Type {type_selected} not managed")
|
175
176
|
urls = sorted(urls, key=lambda url: url.path())
|
176
177
|
|
177
|
-
# if
|
178
|
+
# if there is some angles missing, avoiding setting any angle because they are probably wrong
|
178
179
|
# this will probably fail with EDF but this is legacy
|
179
|
-
if len(
|
180
|
-
|
180
|
+
if len(urls_to_angles) != len(urls):
|
181
|
+
urls_to_angles.clear()
|
181
182
|
|
182
|
-
for
|
183
|
-
if len(
|
184
|
-
text = f"angle={
|
183
|
+
for url in urls:
|
184
|
+
if len(urls_to_angles) > 0:
|
185
|
+
text = f"angle={urls_to_angles[url.path()]}&"
|
185
186
|
else:
|
186
187
|
text = ""
|
187
188
|
if url.data_slice() is not None:
|